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:
authorDalai Felinto <dalai@blender.org>2020-12-17 19:39:49 +0300
committerDalai Felinto <dalai@blender.org>2020-12-17 19:39:49 +0300
commit42b0389a9b05e51e17ba79540e88cd446b52fae7 (patch)
treeee17716640f1b1133ae2ab4ca65cd92a63d09f15
parentc5a17d5ea1ff786cb91cbcf3f12cd02f730c4143 (diff)
parente7b698327cd91b371ff4fd43d1c117637224fded (diff)
Merge remote-tracking branch 'origin/master' into geometry-nodesgeometry-nodes
-rw-r--r--CMakeLists.txt20
-rw-r--r--build_files/cmake/macros.cmake48
-rw-r--r--build_files/cmake/platform/platform_win32.cmake4
-rw-r--r--doc/doxygen/doxygen.source.h4
-rw-r--r--extern/README4
-rw-r--r--extern/audaspace/blender_config.cmake2
-rw-r--r--extern/draco/draco/CMakeLists.txt1
-rw-r--r--extern/mantaflow/CMakeLists.txt7
-rw-r--r--intern/cycles/blender/addon/properties.py2
-rw-r--r--intern/cycles/blender/blender_device.cpp3
-rw-r--r--intern/cycles/bvh/CMakeLists.txt2
-rw-r--r--intern/cycles/bvh/bvh.cpp432
-rw-r--r--intern/cycles/bvh/bvh.h48
-rw-r--r--intern/cycles/bvh/bvh2.cpp409
-rw-r--r--intern/cycles/bvh/bvh2.h39
-rw-r--r--intern/cycles/bvh/bvh_embree.cpp329
-rw-r--r--intern/cycles/bvh/bvh_embree.h32
-rw-r--r--intern/cycles/bvh/bvh_multi.cpp37
-rw-r--r--intern/cycles/bvh/bvh_multi.h39
-rw-r--r--intern/cycles/bvh/bvh_optix.cpp204
-rw-r--r--intern/cycles/bvh/bvh_optix.h28
-rw-r--r--intern/cycles/device/cuda/device_cuda.h1
-rw-r--r--intern/cycles/device/cuda/device_cuda_impl.cpp22
-rw-r--r--intern/cycles/device/device.cpp15
-rw-r--r--intern/cycles/device/device.h11
-rw-r--r--intern/cycles/device/device_cpu.cpp33
-rw-r--r--intern/cycles/device/device_multi.cpp123
-rw-r--r--intern/cycles/device/device_optix.cpp463
-rw-r--r--intern/cycles/device/opencl/opencl_util.cpp22
-rw-r--r--intern/cycles/kernel/bvh/bvh_embree.h6
-rw-r--r--intern/cycles/kernel/kernel_types.h4
-rw-r--r--intern/cycles/render/geometry.cpp88
-rw-r--r--intern/cycles/render/geometry.h3
-rw-r--r--intern/cycles/render/hair.cpp35
-rw-r--r--intern/cycles/render/hair.h2
-rw-r--r--intern/cycles/render/image.cpp4
-rw-r--r--intern/cycles/render/mesh.cpp31
-rw-r--r--intern/cycles/render/mesh.h5
-rw-r--r--intern/cycles/render/scene.cpp5
-rw-r--r--intern/cycles/render/scene.h2
-rw-r--r--intern/cycles/render/shader.cpp11
-rw-r--r--intern/ghost/CMakeLists.txt14
-rw-r--r--intern/ghost/GHOST_C-api.h2
-rw-r--r--intern/ghost/GHOST_ISystemPaths.h6
-rw-r--r--intern/ghost/GHOST_Path-api.h6
-rw-r--r--intern/ghost/GHOST_Types.h10
-rw-r--r--intern/ghost/intern/GHOST_Path-api.cpp6
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm11
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsCocoa.h6
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsCocoa.mm52
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsUnix.cpp59
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsUnix.h6
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsWin32.cpp45
-rw-r--r--intern/ghost/intern/GHOST_SystemPathsWin32.h6
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp4
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp2
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp69
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h14
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/userdef/userdef_default.c1
-rw-r--r--release/license/THIRD-PARTY-LICENSES.txt3973
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils.py4
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_cli.py10
-rw-r--r--release/scripts/modules/bl_keymap_utils/io.py11
-rw-r--r--release/scripts/modules/bpy_extras/__init__.py1
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py63
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py5
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py11
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/anim.py2
-rw-r--r--release/scripts/startup/bl_operators/assets.py75
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py3
-rw-r--r--release/scripts/startup/bl_operators/object.py4
-rw-r--r--release/scripts/startup/bl_operators/object_align.py4
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py5
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_view_layer.py7
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py222
-rw-r--r--release/scripts/startup/bl_ui/space_image.py4
-rw-r--r--release/scripts/startup/bl_ui/space_node.py1
-rw-r--r--release/scripts/startup/bl_ui/space_properties.py6
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py135
-rw-r--r--release/scripts/startup/bl_ui/space_time.py1
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py8
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py54
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py4
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py1
-rw-r--r--release/scripts/startup/nodeitems_builtins.py5
-rw-r--r--release/steam/README.md70
-rw-r--r--release/steam/blender_app_build.vdf.template17
-rw-r--r--release/steam/create_steam_builds.py397
-rw-r--r--release/steam/depot_build_linux.vdf.template31
-rw-r--r--release/steam/depot_build_macos.vdf.template30
-rw-r--r--release/steam/depot_build_win.vdf.template31
-rw-r--r--source/blender/CMakeLists.txt2
-rw-r--r--source/blender/blenfont/intern/blf_dir.c11
-rw-r--r--source/blender/blenkernel/BKE_action.h4
-rw-r--r--source/blender/blenkernel/BKE_anim_visualization.h4
-rw-r--r--source/blender/blenkernel/BKE_animsys.h2
-rw-r--r--source/blender/blenkernel/BKE_appdir.h2
-rw-r--r--source/blender/blenkernel/BKE_asset.h59
-rw-r--r--source/blender/blenkernel/BKE_attribute.h1
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh6
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_boids.h23
-rw-r--r--source/blender/blenkernel/BKE_collision.h22
-rw-r--r--source/blender/blenkernel/BKE_constraint.h8
-rw-r--r--source/blender/blenkernel/BKE_context.h6
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.h11
-rw-r--r--source/blender/blenkernel/BKE_curve.h8
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h2
-rw-r--r--source/blender/blenkernel/BKE_effect.h4
-rw-r--r--source/blender/blenkernel/BKE_freestyle.h37
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h18
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh25
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h4
-rw-r--r--source/blender/blenkernel/BKE_gpencil_curve.h4
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h8
-rw-r--r--source/blender/blenkernel/BKE_gpencil_modifier.h6
-rw-r--r--source/blender/blenkernel/BKE_icons.h31
-rw-r--r--source/blender/blenkernel/BKE_idprop.h78
-rw-r--r--source/blender/blenkernel/BKE_ipo.h1
-rw-r--r--source/blender/blenkernel/BKE_lattice.h1
-rw-r--r--source/blender/blenkernel/BKE_layer.h17
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h3
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h1
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh.h3
-rw-r--r--source/blender/blenkernel/BKE_mesh_fair.h56
-rw-r--r--source/blender/blenkernel/BKE_mesh_runtime.h4
-rw-r--r--source/blender/blenkernel/BKE_modifier.h16
-rw-r--r--source/blender/blenkernel/BKE_multires.h3
-rw-r--r--source/blender/blenkernel/BKE_node.h7
-rw-r--r--source/blender/blenkernel/BKE_paint.h10
-rw-r--r--source/blender/blenkernel/BKE_particle.h7
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h1
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh24
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h15
-rw-r--r--source/blender/blenkernel/BKE_preferences.h56
-rw-r--r--source/blender/blenkernel/BKE_screen.h1
-rw-r--r--source/blender/blenkernel/BKE_sequencer_offscreen.h13
-rw-r--r--source/blender/blenkernel/BKE_shader_fx.h7
-rw-r--r--source/blender/blenkernel/BKE_simulation.h3
-rw-r--r--source/blender/blenkernel/BKE_speaker.h1
-rw-r--r--source/blender/blenkernel/BKE_studiolight.h2
-rw-r--r--source/blender/blenkernel/BKE_text_suggestions.h6
-rw-r--r--source/blender/blenkernel/CMakeLists.txt17
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc (renamed from source/blender/blenkernel/intern/DerivedMesh.c)454
-rw-r--r--source/blender/blenkernel/intern/anim_data.c1
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c79
-rw-r--r--source/blender/blenkernel/intern/appdir.c93
-rw-r--r--source/blender/blenkernel/intern/armature.c2
-rw-r--r--source/blender/blenkernel/intern/asset.c151
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc52
-rw-r--r--source/blender/blenkernel/intern/blender.c1
-rw-r--r--source/blender/blenkernel/intern/blendfile.c3
-rw-r--r--source/blender/blenkernel/intern/boids.c1
-rw-r--r--source/blender/blenkernel/intern/brush.c10
-rw-r--r--source/blender/blenkernel/intern/collection.c25
-rw-r--r--source/blender/blenkernel/intern/constraint.c2
-rw-r--r--source/blender/blenkernel/intern/context.c12
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.c87
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc190
-rw-r--r--source/blender/blenkernel/intern/customdata.c16
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c1
-rw-r--r--source/blender/blenkernel/intern/font.c8
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc46
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c3
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c195
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c2
-rw-r--r--source/blender/blenkernel/intern/icons.cc (renamed from source/blender/blenkernel/intern/icons.c)370
-rw-r--r--source/blender/blenkernel/intern/idprop_utils.c2
-rw-r--r--source/blender/blenkernel/intern/idtype.c1
-rw-r--r--source/blender/blenkernel/intern/image.c18
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c2
-rw-r--r--source/blender/blenkernel/intern/ipo.c25
-rw-r--r--source/blender/blenkernel/intern/key.c15
-rw-r--r--source/blender/blenkernel/intern/layer.c2
-rw-r--r--source/blender/blenkernel/intern/lib_id.c12
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c5
-rw-r--r--source/blender/blenkernel/intern/lib_override.c38
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c3
-rw-r--r--source/blender/blenkernel/intern/mesh.c51
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc505
-rw-r--r--source/blender/blenkernel/intern/modifier.c3
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.h2
-rw-r--r--source/blender/blenkernel/intern/nla.c7
-rw-r--r--source/blender/blenkernel/intern/node.c73
-rw-r--r--source/blender/blenkernel/intern/object.c28
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c53
-rw-r--r--source/blender/blenkernel/intern/object_update.c1
-rw-r--r--source/blender/blenkernel/intern/particle.c1
-rw-r--r--source/blender/blenkernel/intern/particle_child.c1
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c1
-rw-r--r--source/blender/blenkernel/intern/particle_system.c29
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc36
-rw-r--r--source/blender/blenkernel/intern/preferences.c119
-rw-r--r--source/blender/blenkernel/intern/scene.c14
-rw-r--r--source/blender/blenkernel/intern/screen.c12
-rw-r--r--source/blender/blenkernel/intern/softbody.c1
-rw-r--r--source/blender/blenkernel/intern/sound.c8
-rw-r--r--source/blender/blenkernel/intern/text.c13
-rw-r--r--source/blender/blenkernel/intern/volume.cc8
-rw-r--r--source/blender/blenkernel/particle_private.h4
-rw-r--r--source/blender/blenlib/BLI_array.hh4
-rw-r--r--source/blender/blenlib/BLI_hash.h58
-rw-r--r--source/blender/blenlib/BLI_index_range.hh45
-rw-r--r--source/blender/blenlib/BLI_inplace_priority_queue.hh304
-rw-r--r--source/blender/blenlib/BLI_kdopbvh.h1
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh19
-rw-r--r--source/blender/blenlib/BLI_span.hh129
-rw-r--r--source/blender/blenlib/BLI_string_ref.hh126
-rw-r--r--source/blender/blenlib/BLI_threads.h1
-rw-r--r--source/blender/blenlib/BLI_vector.hh4
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c19
-rw-r--r--source/blender/blenlib/intern/math_rotation.c17
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc7
-rw-r--r--source/blender/blenlib/intern/path_util.c99
-rw-r--r--source/blender/blenlib/tests/BLI_index_range_test.cc7
-rw-r--r--source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc113
-rw-r--r--source/blender/blenlib/tests/BLI_math_rotation_test.cc21
-rw-r--r--source/blender/blenlib/tests/BLI_memory_utils_test.cc11
-rw-r--r--source/blender/blenlib/tests/BLI_span_test.cc15
-rw-r--r--source/blender/blenlib/tests/BLI_string_ref_test.cc8
-rw-r--r--source/blender/blenloader/BLO_read_write.h1
-rw-r--r--source/blender/blenloader/BLO_readfile.h8
-rw-r--r--source/blender/blenloader/intern/readblenentry.c49
-rw-r--r--source/blender/blenloader/intern/readfile.c45
-rw-r--r--source/blender/blenloader/intern/readfile.h9
-rw-r--r--source/blender/blenloader/intern/versioning_250.c1
-rw-r--r--source/blender/blenloader/intern/versioning_260.c2
-rw-r--r--source/blender/blenloader/intern/versioning_270.c5
-rw-r--r--source/blender/blenloader/intern/versioning_290.c241
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c15
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c12
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c1
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c8
-rw-r--r--source/blender/blenloader/intern/writefile.c10
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cpp53
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc7
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc1
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc22
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.h2
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c65
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.h4
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h1
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c26
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c63
-rw-r--r--source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl7
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h3
-rw-r--r--source/blender/draw/engines/image/image_private.h2
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl3
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl2
-rw-r--r--source/blender/draw/intern/DRW_render.h1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c1
-rw-r--r--source/blender/draw/intern/draw_common.h2
-rw-r--r--source/blender/draw/intern/draw_instance_data.c10
-rw-r--r--source/blender/draw/intern/draw_manager.c9
-rw-r--r--source/blender/editors/CMakeLists.txt1
-rw-r--r--source/blender/editors/animation/anim_ops.c2
-rw-r--r--source/blender/editors/armature/armature_edit.c2
-rw-r--r--source/blender/editors/armature/editarmature_undo.c2
-rw-r--r--source/blender/editors/asset/CMakeLists.txt39
-rw-r--r--source/blender/editors/asset/asset_edit.c69
-rw-r--r--source/blender/editors/asset/asset_ops.c238
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c69
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c1
-rw-r--r--source/blender/editors/include/ED_armature.h1
-rw-r--r--source/blender/editors/include/ED_asset.h39
-rw-r--r--source/blender/editors/include/ED_fileselect.h9
-rw-r--r--source/blender/editors/include/ED_gpencil.h1
-rw-r--r--source/blender/editors/include/ED_image.h2
-rw-r--r--source/blender/editors/include/ED_info.h8
-rw-r--r--source/blender/editors/include/ED_object.h1
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_transform.h2
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h1
-rw-r--r--source/blender/editors/include/ED_util.h2
-rw-r--r--source/blender/editors/include/ED_util_imbuf.h1
-rw-r--r--source/blender/editors/include/ED_uvedit.h9
-rw-r--r--source/blender/editors/include/ED_view3d.h10
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/interface.c46
-rw-r--r--source/blender/editors/interface/interface_context_menu.c27
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c1
-rw-r--r--source/blender/editors/interface/interface_handlers.c14
-rw-r--r--source/blender/editors/interface/interface_icons.c48
-rw-r--r--source/blender/editors/interface/interface_layout.c7
-rw-r--r--source/blender/editors/interface/interface_style.c8
-rw-r--r--source/blender/editors/interface/interface_templates.c9
-rw-r--r--source/blender/editors/interface/view2d_ops.c72
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c2
-rw-r--r--source/blender/editors/mesh/editmesh_select.c3
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c1
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c8
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c1
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c6
-rw-r--r--source/blender/editors/metaball/editmball_undo.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt3
-rw-r--r--source/blender/editors/object/object_add.c9
-rw-r--r--source/blender/editors/object/object_bake.c1
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c4
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c25
-rw-r--r--source/blender/editors/object/object_shader_fx.c54
-rw-r--r--source/blender/editors/object/object_vgroup.c3
-rw-r--r--source/blender/editors/physics/particle_object.c3
-rw-r--r--source/blender/editors/physics/rigidbody_constraint.c1
-rw-r--r--source/blender/editors/physics/rigidbody_object.c1
-rw-r--r--source/blender/editors/render/render_internal.c18
-rw-r--r--source/blender/editors/render/render_preview.c301
-rw-r--r--source/blender/editors/screen/screen_context.c4
-rw-r--r--source/blender/editors/screen/screen_edit.c186
-rw-r--r--source/blender/editors/screen/screen_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h1
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c197
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c37
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c91
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h11
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_action/action_select.c4
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c11
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h1
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_console/space_console.c4
-rw-r--r--source/blender/editors/space_file/file_draw.c186
-rw-r--r--source/blender/editors/space_file/file_intern.h4
-rw-r--r--source/blender/editors/space_file/file_ops.c50
-rw-r--r--source/blender/editors/space_file/file_utils.c17
-rw-r--r--source/blender/editors/space_file/filelist.c643
-rw-r--r--source/blender/editors/space_file/filelist.h19
-rw-r--r--source/blender/editors/space_file/filesel.c191
-rw-r--r--source/blender/editors/space_file/space_file.c247
-rw-r--r--source/blender/editors/space_graph/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_graph/graph_edit.c4
-rw-r--r--source/blender/editors/space_graph/graph_select.c4
-rw-r--r--source/blender/editors/space_graph/graph_view.c2
-rw-r--r--source/blender/editors/space_info/info_report.c2
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_node/drawnode.c62
-rw-r--r--source/blender/editors/space_node/node_draw.c61
-rw-r--r--source/blender/editors/space_node/node_edit.c344
-rw-r--r--source/blender/editors/space_node/node_view.c2
-rw-r--r--source/blender/editors/space_node/space_node.c8
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt7
-rw-r--r--source/blender/editors/space_outliner/outliner_context.c73
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c22
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h7
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c29
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c6
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_libraries.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_view_layer.cc1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_nla.hh1
-rw-r--r--source/blender/editors/space_script/script_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c29
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c287
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c292
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h3
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c4
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c6
-rw-r--r--source/blender/editors/space_text/space_text.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_userpref/userpref_ops.c63
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c26
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c35
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c28
-rw-r--r--source/blender/editors/transform/CMakeLists.txt2
-rw-r--r--source/blender/editors/transform/transform.h2
-rw-r--r--source/blender/editors/transform/transform_convert.h3
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c3
-rw-r--r--source/blender/editors/transform/transform_ops.c2
-rw-r--r--source/blender/editors/transform/transform_snap.h2
-rw-r--r--source/blender/editors/undo/memfile_undo.c1
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_util.c105
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt2
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h1
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c15
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c1
-rw-r--r--source/blender/gpu/GPU_shader.h2
-rw-r--r--source/blender/gpu/GPU_texture.h8
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h3
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h2
-rw-r--r--source/blender/gpu/intern/gpu_uniform_buffer_private.hh2
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c6
-rw-r--r--source/blender/imbuf/IMB_imbuf.h2
-rw-r--r--source/blender/imbuf/intern/anim_movie.c10
-rw-r--r--source/blender/imbuf/intern/jpeg.c2
-rw-r--r--source/blender/imbuf/intern/metadata.c2
-rw-r--r--source/blender/imbuf/intern/png.c2
-rw-r--r--source/blender/imbuf/intern/thumbs_blend.c10
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.h3
-rw-r--r--source/blender/io/alembic/intern/abc_reader_archive.h1
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc1
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp5
-rw-r--r--source/blender/io/collada/ArmatureImporter.cpp4
-rw-r--r--source/blender/io/collada/collada_utils.cpp4
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h2
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc2
-rw-r--r--source/blender/io/usd/intern/usd_exporter_context.h1
-rw-r--r--source/blender/io/usd/intern/usd_hierarchy_iterator.h1
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h1
-rw-r--r--source/blender/io/usd/usd.h1
-rw-r--r--source/blender/makesdna/DNA_ID.h9
-rw-r--r--source/blender/makesdna/DNA_anim_types.h13
-rw-r--r--source/blender/makesdna/DNA_asset_defaults.h37
-rw-r--r--source/blender/makesdna/DNA_asset_types.h62
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h614
-rw-r--r--source/blender/makesdna/DNA_brush_types.h587
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h8
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_layer_types.h4
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h4
-rw-r--r--source/blender/makesdna/DNA_movieclip_types.h4
-rw-r--r--source/blender/makesdna/DNA_node_types.h47
-rw-r--r--source/blender/makesdna/DNA_object_types.h15
-rw-r--r--source/blender/makesdna/DNA_scene_types.h27
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h8
-rw-r--r--source/blender/makesdna/DNA_space_types.h130
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h14
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h6
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h2
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c10
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt3
-rw-r--r--source/blender/makesrna/intern/makesrna.c3
-rw-r--r--source/blender/makesrna/intern/rna_ID.c11
-rw-r--r--source/blender/makesrna/intern/rna_access.c9
-rw-r--r--source/blender/makesrna/intern/rna_asset.c228
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c1
-rw-r--r--source/blender/makesrna/intern/rna_brush.c2
-rw-r--r--source/blender/makesrna/intern/rna_camera.c2
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c4
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c2
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c10
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c6
-rw-r--r--source/blender/makesrna/intern/rna_internal.h3
-rw-r--r--source/blender/makesrna/intern/rna_light.c2
-rw-r--r--source/blender/makesrna/intern/rna_main.c4
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_material.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c18
-rw-r--r--source/blender/makesrna/intern/rna_nla.c2
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c258
-rw-r--r--source/blender/makesrna/intern/rna_object.c12
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c10
-rw-r--r--source/blender/makesrna/intern/rna_particle.c2
-rw-r--r--source/blender/makesrna/intern/rna_pose.c11
-rw-r--r--source/blender/makesrna/intern/rna_rna.c2
-rw-r--r--source/blender/makesrna/intern/rna_scene.c58
-rw-r--r--source/blender/makesrna/intern/rna_screen.c4
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c5
-rw-r--r--source/blender/makesrna/intern/rna_space.c339
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c63
-rw-r--r--source/blender/modifiers/CMakeLists.txt3
-rw-r--r--source/blender/modifiers/MOD_nodes.h2
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c2
-rw-r--r--source/blender/modifiers/intern/MOD_array.c2
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c2
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c2
-rw-r--r--source/blender/modifiers/intern/MOD_build.c2
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c2
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c5
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c4
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c2
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c2
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c2
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c2
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c2
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c2
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c2
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c2
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c2
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c2
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c2
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c2
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c2
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c2
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc50
-rw-r--r--source/blender/modifiers/intern/MOD_none.c2
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c2
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c2
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c2
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c2
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c2
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c6
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c3
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c2
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c2
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c2
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c3
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c2
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c3
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c1
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c2
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c2
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c2
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c2
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c8
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c2
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c2
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc2
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c2
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c2
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c2
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c2
-rw-r--r--source/blender/nodes/CMakeLists.txt18
-rw-r--r--source/blender/nodes/NOD_geometry.h5
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh15
-rw-r--r--source/blender/nodes/NOD_static_types.h9
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.c178
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc141
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc221
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc)112
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc206
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc281
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc63
-rw-r--r--source/blender/nodes/intern/math_functions.cc2
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc62
-rw-r--r--source/blender/nodes/intern/node_socket.cc31
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc4
-rw-r--r--source/blender/nodes/intern/node_util.c8
-rw-r--r--source/blender/python/BPY_extern.h1
-rw-r--r--source/blender/python/generic/idprop_py_api.c2
-rw-r--r--source/blender/python/gpu/gpu_py_batch.c56
-rw-r--r--source/blender/python/gpu/gpu_py_element.c8
-rw-r--r--source/blender/python/gpu/gpu_py_matrix.c181
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c87
-rw-r--r--source/blender/python/gpu/gpu_py_select.c14
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c228
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.c37
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.c40
-rw-r--r--source/blender/python/intern/bpy_props.c2
-rw-r--r--source/blender/render/CMakeLists.txt2
-rw-r--r--source/blender/render/RE_pipeline.h2
-rw-r--r--source/blender/render/intern/engine.c4
-rw-r--r--source/blender/render/intern/pipeline.c6
-rw-r--r--source/blender/render/intern/render_result.c47
-rw-r--r--source/blender/render/intern/render_result.h1
-rw-r--r--source/blender/render/intern/texture_pointdensity.c1
-rw-r--r--source/blender/sequencer/CMakeLists.txt2
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h57
-rw-r--r--source/blender/sequencer/intern/effects.c1
-rw-r--r--source/blender/sequencer/intern/effects.h2
-rw-r--r--source/blender/sequencer/intern/image_cache.h2
-rw-r--r--source/blender/sequencer/intern/multiview.h7
-rw-r--r--source/blender/sequencer/intern/prefetch.h4
-rw-r--r--source/blender/sequencer/intern/proxy.h2
-rw-r--r--source/blender/sequencer/intern/render.c35
-rw-r--r--source/blender/sequencer/intern/render.h1
-rw-r--r--source/blender/sequencer/intern/sequencer.c47
-rw-r--r--source/blender/sequencer/intern/strip_add.c15
-rw-r--r--source/blender/sequencer/intern/strip_edit.c35
-rw-r--r--source/blender/sequencer/intern/strip_time.c82
-rw-r--r--source/blender/sequencer/intern/strip_time.h12
-rw-r--r--source/blender/sequencer/intern/strip_transform.c30
-rw-r--r--source/blender/sequencer/intern/utils.c34
-rw-r--r--source/blender/sequencer/intern/utils.h1
-rw-r--r--source/blender/shader_fx/intern/FX_ui_common.c56
-rw-r--r--source/blender/simulation/SIM_mass_spring.h2
-rw-r--r--source/blender/windowmanager/WM_api.h10
-rw-r--r--source/blender/windowmanager/WM_types.h51
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c102
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c7
-rw-r--r--source/blender/windowmanager/intern/wm_files.c17
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c2
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c4
-rw-r--r--source/blender/windowmanager/wm.h1
-rw-r--r--source/creator/CMakeLists.txt4
-rw-r--r--tests/python/CMakeLists.txt42
-rw-r--r--tests/python/bevel_operator.py366
-rw-r--r--tests/python/boolean_operator.py48
-rw-r--r--tests/python/deform_modifiers.py119
-rw-r--r--tests/python/modifiers.py352
-rw-r--r--tests/python/modules/mesh_test.py557
-rw-r--r--tests/python/operators.py181
-rw-r--r--tests/python/physics_cloth.py28
-rw-r--r--tests/python/physics_dynamic_paint.py58
-rw-r--r--tests/python/physics_ocean.py54
-rw-r--r--tests/python/physics_particle_instance.py56
-rw-r--r--tests/python/physics_particle_system.py55
-rw-r--r--tests/python/physics_softbody.py23
656 files changed, 19910 insertions, 5969 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0d7cf4e325c..c8ae562f6ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -347,16 +347,21 @@ if(UNIX AND NOT APPLE)
endif()
option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON)
+
+if((WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE) OR WITH_MOD_FLUID)
+ option(WITH_PYTHON_NUMPY "Include NumPy in Blender (used by Audaspace and Mantaflow)" ON)
+endif()
+
if(WIN32 OR APPLE)
# Windows and macOS have this bundled with Python libraries.
-elseif(WITH_PYTHON_INSTALL OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
+elseif(WITH_PYTHON_INSTALL OR WITH_PYTHON_NUMPY)
set(PYTHON_NUMPY_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'numpy' module")
mark_as_advanced(PYTHON_NUMPY_PATH)
- set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
+ set(PYTHON_NUMPY_INCLUDE_DIRS "" CACHE PATH "Path to the include directory of the NumPy module")
mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
endif()
if(WITH_PYTHON_INSTALL)
- option(WITH_PYTHON_INSTALL_NUMPY "Copy system numpy into the blender install folder" ON)
+ option(WITH_PYTHON_INSTALL_NUMPY "Copy system NumPy into the blender install folder" ON)
if(UNIX AND NOT APPLE)
option(WITH_PYTHON_INSTALL_REQUESTS "Copy system requests into the blender install folder" ON)
@@ -1621,19 +1626,16 @@ if(WITH_PYTHON)
if(WIN32 OR APPLE)
# Windows and macOS have this bundled with Python libraries.
- elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE))
+ elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR WITH_PYTHON_NUMPY)
if(("${PYTHON_NUMPY_PATH}" STREQUAL "") OR (${PYTHON_NUMPY_PATH} MATCHES NOTFOUND))
- find_python_package(numpy)
- unset(PYTHON_NUMPY_INCLUDE_DIRS CACHE)
- set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module")
- mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS)
+ find_python_package(numpy "core/include")
endif()
endif()
if(WIN32 OR APPLE)
# pass, we have this in lib/python/site-packages
elseif(WITH_PYTHON_INSTALL_REQUESTS)
- find_python_package(requests)
+ find_python_package(requests "")
endif()
endif()
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 202b44f611c..9febeaa6889 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -1139,6 +1139,7 @@ endfunction()
function(find_python_package
package
+ relative_include_dir
)
string(TOUPPER ${package} _upper_package)
@@ -1170,7 +1171,10 @@ function(find_python_package
dist-packages
vendor-packages
NO_DEFAULT_PATH
+ DOC
+ "Path to python site-packages or dist-packages containing '${package}' module"
)
+ mark_as_advanced(PYTHON_${_upper_package}_PATH)
if(NOT EXISTS "${PYTHON_${_upper_package}_PATH}")
message(WARNING
@@ -1188,6 +1192,50 @@ function(find_python_package
set(WITH_PYTHON_INSTALL_${_upper_package} OFF PARENT_SCOPE)
else()
message(STATUS "${package} found at '${PYTHON_${_upper_package}_PATH}'")
+
+ if(NOT "${relative_include_dir}" STREQUAL "")
+ set(_relative_include_dir "${package}/${relative_include_dir}")
+ unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
+ find_path(PYTHON_${_upper_package}_INCLUDE_DIRS
+ NAMES
+ "${_relative_include_dir}"
+ HINTS
+ "${PYTHON_LIBPATH}/"
+ "${PYTHON_LIBPATH}/python${PYTHON_VERSION}/"
+ "${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/"
+ PATH_SUFFIXES
+ "site-packages/"
+ "dist-packages/"
+ "vendor-packages/"
+ NO_DEFAULT_PATH
+ DOC
+ "Path to python site-packages or dist-packages containing '${package}' module header files"
+ )
+ mark_as_advanced(PYTHON_${_upper_package}_INCLUDE_DIRS)
+
+ if(NOT EXISTS "${PYTHON_${_upper_package}_INCLUDE_DIRS}")
+ message(WARNING
+ "Python package '${package}' include dir path could not be found in:\n"
+ "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/site-packages/${_relative_include_dir}', "
+ "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/site-packages/${_relative_include_dir}', "
+ "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/dist-packages/${_relative_include_dir}', "
+ "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/dist-packages/${_relative_include_dir}', "
+ "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/vendor-packages/${_relative_include_dir}', "
+ "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/vendor-packages/${_relative_include_dir}', "
+ "\n"
+ "The 'WITH_PYTHON_${_upper_package}' option will be disabled.\n"
+ "The build will be usable, only add-ons that depend on this package won't be functional."
+ )
+ set(WITH_PYTHON_${_upper_package} OFF PARENT_SCOPE)
+ else()
+ set(_temp "${PYTHON_${_upper_package}_INCLUDE_DIRS}/${package}/${relative_include_dir}")
+ unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE)
+ set(PYTHON_${_upper_package}_INCLUDE_DIRS "${_temp}"
+ CACHE PATH "Path to the include directory of the ${package} module")
+
+ message(STATUS "${package} include files found at '${PYTHON_${_upper_package}_INCLUDE_DIRS}'")
+ endif()
+ endif()
endif()
endif()
endfunction()
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index dd8b286c689..016ae58fde4 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -739,7 +739,7 @@ if(WINDOWS_PYTHON_DEBUG)
string(REPLACE "/" "\\" _group_path "${_source_path}")
source_group("${_group_path}" FILES "${_source}")
endforeach()
-
+
# If the user scripts env var is set, include scripts from there otherwise
# include user scripts in the profile folder.
if(DEFINED ENV{BLENDER_USER_SCRIPTS})
@@ -750,7 +750,7 @@ if(WINDOWS_PYTHON_DEBUG)
# Include the user scripts from the profile folder in the blender_python_user_scripts project.
set(USER_SCRIPTS_ROOT "$ENV{appdata}/blender foundation/blender/${BLENDER_VERSION}/scripts")
endif()
-
+
file(TO_CMAKE_PATH ${USER_SCRIPTS_ROOT} USER_SCRIPTS_ROOT)
FILE(GLOB_RECURSE inFiles "${USER_SCRIPTS_ROOT}/*.*" )
ADD_CUSTOM_TARGET(blender_python_user_scripts SOURCES ${inFiles})
diff --git a/doc/doxygen/doxygen.source.h b/doc/doxygen/doxygen.source.h
index 613e513bcc6..510f3fe8ffe 100644
--- a/doc/doxygen/doxygen.source.h
+++ b/doc/doxygen/doxygen.source.h
@@ -121,6 +121,10 @@
* \ingroup editors
*/
+/** \defgroup edasset asset
+ * \ingroup editors
+ */
+
/** \defgroup edcurve curve
* \ingroup editors
*/
diff --git a/extern/README b/extern/README
new file mode 100644
index 00000000000..f904fc3e41e
--- /dev/null
+++ b/extern/README
@@ -0,0 +1,4 @@
+When updating a library remember to:
+
+* Update the README.blender with the corresponding version.
+* Update the THIRD-PARTY-LICENSE.txt document
diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake
index 1f8f85b9868..12810e2b044 100644
--- a/extern/audaspace/blender_config.cmake
+++ b/extern/audaspace/blender_config.cmake
@@ -24,6 +24,6 @@ set(JACK_FOUND ${WITH_JACK})
set(LIBSNDFILE_FOUND ${WITH_CODEC_SNDFILE})
set(OPENAL_FOUND ${WITH_OPENAL})
set(PYTHONLIBS_FOUND TRUE)
-set(NUMPY_FOUND TRUE)
+set(NUMPY_FOUND ${WITH_PYTHON_NUMPY})
set(NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_INCLUDE_DIRS})
set(SDL_FOUND ${WITH_SDL})
diff --git a/extern/draco/draco/CMakeLists.txt b/extern/draco/draco/CMakeLists.txt
index 6f9ffce6b48..d775b39d3ac 100644
--- a/extern/draco/draco/CMakeLists.txt
+++ b/extern/draco/draco/CMakeLists.txt
@@ -268,4 +268,3 @@ set(INC
)
blender_add_lib(draco "${SRC}" "${INC}" "" "${LIB}")
-
diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt
index ee155ee8636..ccf272650e3 100644
--- a/extern/mantaflow/CMakeLists.txt
+++ b/extern/mantaflow/CMakeLists.txt
@@ -85,7 +85,7 @@ if(WIN32)
add_definitions(-D_USE_MATH_DEFINES)
endif()
-if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
add_definitions(-DNUMPY=1)
endif()
@@ -109,7 +109,7 @@ set(INC_SYS
${ZLIB_INCLUDE_DIRS}
)
-if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
list(APPEND INC_SYS
${PYTHON_NUMPY_INCLUDE_DIRS}
)
@@ -255,8 +255,7 @@ if(WITH_MANTA_DEPENDENCIES)
${MANTA_DEP}/cnpy/cnpy.h
)
endif()
-
-if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY)
+if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY)
list(APPEND SRC
${MANTA_PP}/plugin/numpyconvert.cpp
${MANTA_PP}/plugin/tfplugins.cpp
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 1cb29fc6cb0..2f204b2c658 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
elif entry.type == 'CPU':
cpu_devices.append(entry)
# Extend all GPU devices with CPU.
- if compute_device_type in {'CUDA', 'OPENCL'}:
+ if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}:
devices.extend(cpu_devices)
return devices
diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp
index ffcaef0b2a9..977f8297de1 100644
--- a/intern/cycles/blender/blender_device.cpp
+++ b/intern/cycles/blender/blender_device.cpp
@@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
mask |= DEVICE_MASK_CUDA;
}
else if (compute_device == COMPUTE_DEVICE_OPTIX) {
- /* Cannot use CPU and OptiX device at the same time right now, so replace mask. */
- mask = DEVICE_MASK_OPTIX;
+ mask |= DEVICE_MASK_OPTIX;
}
else if (compute_device == COMPUTE_DEVICE_OPENCL) {
mask |= DEVICE_MASK_OPENCL;
diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt
index 703c69b1797..8cc72359757 100644
--- a/intern/cycles/bvh/CMakeLists.txt
+++ b/intern/cycles/bvh/CMakeLists.txt
@@ -25,6 +25,7 @@ set(SRC
bvh_binning.cpp
bvh_build.cpp
bvh_embree.cpp
+ bvh_multi.cpp
bvh_node.cpp
bvh_optix.cpp
bvh_sort.cpp
@@ -38,6 +39,7 @@ set(SRC_HEADERS
bvh_binning.h
bvh_build.h
bvh_embree.h
+ bvh_multi.h
bvh_node.h
bvh_optix.h
bvh_params.h
diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp
index a51ac4cf4a9..256382e63ba 100644
--- a/intern/cycles/bvh/bvh.cpp
+++ b/intern/cycles/bvh/bvh.cpp
@@ -17,17 +17,11 @@
#include "bvh/bvh.h"
-#include "render/hair.h"
-#include "render/mesh.h"
-#include "render/object.h"
-
#include "bvh/bvh2.h"
-#include "bvh/bvh_build.h"
#include "bvh/bvh_embree.h"
-#include "bvh/bvh_node.h"
+#include "bvh/bvh_multi.h"
#include "bvh/bvh_optix.h"
-#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
@@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN
const char *bvh_layout_name(BVHLayout layout)
{
switch (layout) {
- case BVH_LAYOUT_BVH2:
- return "BVH2";
case BVH_LAYOUT_NONE:
return "NONE";
+ case BVH_LAYOUT_BVH2:
+ return "BVH2";
case BVH_LAYOUT_EMBREE:
return "EMBREE";
case BVH_LAYOUT_OPTIX:
return "OPTIX";
+ case BVH_LAYOUT_MULTI_OPTIX:
+ case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
+ return "MULTI";
case BVH_LAYOUT_ALL:
return "ALL";
}
@@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s
return (BVHLayout)(1 << widest_allowed_layout_mask);
}
-/* Pack Utility */
-
-BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
-{
-}
-
-int BVHStackEntry::encodeIdx() const
-{
- return (node->is_leaf()) ? ~idx : idx;
-}
-
/* BVH */
BVH::BVH(const BVHParams &params_,
@@ -99,24 +85,27 @@ BVH::BVH(const BVHParams &params_,
BVH *BVH::create(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects,
- const Device *device)
+ Device *device)
{
switch (params.bvh_layout) {
case BVH_LAYOUT_BVH2:
return new BVH2(params, geometry, objects);
case BVH_LAYOUT_EMBREE:
#ifdef WITH_EMBREE
- return new BVHEmbree(params, geometry, objects, device);
+ return new BVHEmbree(params, geometry, objects);
#else
- (void)device;
break;
#endif
case BVH_LAYOUT_OPTIX:
#ifdef WITH_OPTIX
- return new BVHOptiX(params, geometry, objects);
+ return new BVHOptiX(params, geometry, objects, device);
#else
+ (void)device;
break;
#endif
+ case BVH_LAYOUT_MULTI_OPTIX:
+ case BVH_LAYOUT_MULTI_OPTIX_EMBREE:
+ return new BVHMulti(params, geometry, objects);
case BVH_LAYOUT_NONE:
case BVH_LAYOUT_ALL:
break;
@@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams &params,
return NULL;
}
-/* Building */
-
-void BVH::build(Progress &progress, Stats *)
-{
- progress.set_substatus("Building BVH");
-
- /* build nodes */
- BVHBuild bvh_build(objects,
- pack.prim_type,
- pack.prim_index,
- pack.prim_object,
- pack.prim_time,
- params,
- progress);
- BVHNode *bvh2_root = bvh_build.run();
-
- if (progress.get_cancel()) {
- if (bvh2_root != NULL) {
- bvh2_root->deleteSubtree();
- }
- return;
- }
-
- /* BVH builder returns tree in a binary mode (with two children per inner
- * node. Need to adopt that for a wider BVH implementations. */
- BVHNode *root = widen_children_nodes(bvh2_root);
- if (root != bvh2_root) {
- bvh2_root->deleteSubtree();
- }
-
- if (progress.get_cancel()) {
- if (root != NULL) {
- root->deleteSubtree();
- }
- return;
- }
-
- /* pack triangles */
- progress.set_substatus("Packing BVH triangles and strands");
- pack_primitives();
-
- if (progress.get_cancel()) {
- root->deleteSubtree();
- return;
- }
-
- /* pack nodes */
- progress.set_substatus("Packing BVH nodes");
- pack_nodes(root);
-
- /* free build nodes */
- root->deleteSubtree();
-}
-
-/* Refitting */
-
-void BVH::refit(Progress &progress)
-{
- progress.set_substatus("Packing BVH primitives");
- pack_primitives();
-
- if (progress.get_cancel())
- return;
-
- progress.set_substatus("Refitting BVH nodes");
- refit_nodes();
-}
-
-void BVH::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
-{
- /* Refit range of primitives. */
- for (int prim = start; prim < end; prim++) {
- int pidx = pack.prim_index[prim];
- int tob = pack.prim_object[prim];
- Object *ob = objects[tob];
-
- if (pidx == -1) {
- /* Object instance. */
- bbox.grow(ob->bounds);
- }
- else {
- /* Primitives. */
- if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
- /* Curves. */
- const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
- int prim_offset = (params.top_level) ? hair->prim_offset : 0;
- Hair::Curve curve = hair->get_curve(pidx - prim_offset);
- int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
-
- curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
-
- /* Motion curves. */
- if (hair->get_use_motion_blur()) {
- Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr) {
- size_t hair_size = hair->get_curve_keys().size();
- size_t steps = hair->get_motion_steps() - 1;
- float3 *key_steps = attr->data_float3();
-
- for (size_t i = 0; i < steps; i++)
- curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
- }
- }
- }
- else {
- /* Triangles. */
- const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
- int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
- Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
- const float3 *vpos = &mesh->verts[0];
-
- triangle.bounds_grow(vpos, bbox);
-
- /* Motion triangles. */
- if (mesh->use_motion_blur) {
- Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr) {
- size_t mesh_size = mesh->verts.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *vert_steps = attr->data_float3();
-
- for (size_t i = 0; i < steps; i++)
- triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
- }
- }
- }
- }
- visibility |= ob->visibility_for_tracing();
- }
-}
-
-/* Triangles */
-
-void BVH::pack_triangle(int idx, float4 tri_verts[3])
-{
- int tob = pack.prim_object[idx];
- assert(tob >= 0 && tob < objects.size());
- const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
-
- int tidx = pack.prim_index[idx];
- Mesh::Triangle t = mesh->get_triangle(tidx);
- const float3 *vpos = &mesh->verts[0];
- float3 v0 = vpos[t.v[0]];
- float3 v1 = vpos[t.v[1]];
- float3 v2 = vpos[t.v[2]];
-
- tri_verts[0] = float3_to_float4(v0);
- tri_verts[1] = float3_to_float4(v1);
- tri_verts[2] = float3_to_float4(v2);
-}
-
-void BVH::pack_primitives()
-{
- const size_t tidx_size = pack.prim_index.size();
- size_t num_prim_triangles = 0;
- /* Count number of triangles primitives in BVH. */
- for (unsigned int i = 0; i < tidx_size; i++) {
- if ((pack.prim_index[i] != -1)) {
- if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
- ++num_prim_triangles;
- }
- }
- }
- /* Reserve size for arrays. */
- pack.prim_tri_index.clear();
- pack.prim_tri_index.resize(tidx_size);
- pack.prim_tri_verts.clear();
- pack.prim_tri_verts.resize(num_prim_triangles * 3);
- pack.prim_visibility.clear();
- pack.prim_visibility.resize(tidx_size);
- /* Fill in all the arrays. */
- size_t prim_triangle_index = 0;
- for (unsigned int i = 0; i < tidx_size; i++) {
- if (pack.prim_index[i] != -1) {
- int tob = pack.prim_object[i];
- Object *ob = objects[tob];
- if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
- pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
- pack.prim_tri_index[i] = 3 * prim_triangle_index;
- ++prim_triangle_index;
- }
- else {
- pack.prim_tri_index[i] = -1;
- }
- pack.prim_visibility[i] = ob->visibility_for_tracing();
- }
- else {
- pack.prim_tri_index[i] = -1;
- pack.prim_visibility[i] = 0;
- }
- }
-}
-
-/* Pack Instances */
-
-void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
-{
- /* Adjust primitive index to point to the triangle in the global array, for
- * geometry with transform applied and already in the top level BVH.
- */
- for (size_t i = 0; i < pack.prim_index.size(); i++) {
- if (pack.prim_index[i] != -1) {
- pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
- }
- }
-
- /* track offsets of instanced BVH data in global array */
- size_t prim_offset = pack.prim_index.size();
- size_t nodes_offset = nodes_size;
- size_t nodes_leaf_offset = leaf_nodes_size;
-
- /* clear array that gives the node indexes for instanced objects */
- pack.object_node.clear();
-
- /* reserve */
- size_t prim_index_size = pack.prim_index.size();
- size_t prim_tri_verts_size = pack.prim_tri_verts.size();
-
- size_t pack_prim_index_offset = prim_index_size;
- size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
- size_t pack_nodes_offset = nodes_size;
- size_t pack_leaf_nodes_offset = leaf_nodes_size;
- size_t object_offset = 0;
-
- foreach (Geometry *geom, geometry) {
- BVH *bvh = geom->bvh;
-
- if (geom->need_build_bvh(params.bvh_layout)) {
- prim_index_size += bvh->pack.prim_index.size();
- prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
- nodes_size += bvh->pack.nodes.size();
- leaf_nodes_size += bvh->pack.leaf_nodes.size();
- }
- }
-
- pack.prim_index.resize(prim_index_size);
- pack.prim_type.resize(prim_index_size);
- pack.prim_object.resize(prim_index_size);
- pack.prim_visibility.resize(prim_index_size);
- pack.prim_tri_verts.resize(prim_tri_verts_size);
- pack.prim_tri_index.resize(prim_index_size);
- pack.nodes.resize(nodes_size);
- pack.leaf_nodes.resize(leaf_nodes_size);
- pack.object_node.resize(objects.size());
-
- if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
- pack.prim_time.resize(prim_index_size);
- }
-
- int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
- int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
- int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
- uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
- float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
- uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
- int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
- int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
- float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
-
- map<Geometry *, int> geometry_map;
-
- /* merge */
- foreach (Object *ob, objects) {
- Geometry *geom = ob->get_geometry();
-
- /* We assume that if mesh doesn't need own BVH it was already included
- * into a top-level BVH and no packing here is needed.
- */
- if (!geom->need_build_bvh(params.bvh_layout)) {
- pack.object_node[object_offset++] = 0;
- continue;
- }
-
- /* if mesh already added once, don't add it again, but used set
- * node offset for this object */
- map<Geometry *, int>::iterator it = geometry_map.find(geom);
-
- if (geometry_map.find(geom) != geometry_map.end()) {
- int noffset = it->second;
- pack.object_node[object_offset++] = noffset;
- continue;
- }
-
- BVH *bvh = geom->bvh;
-
- int noffset = nodes_offset;
- int noffset_leaf = nodes_leaf_offset;
- int geom_prim_offset = geom->prim_offset;
-
- /* fill in node indexes for instances */
- if (bvh->pack.root_index == -1)
- pack.object_node[object_offset++] = -noffset_leaf - 1;
- else
- pack.object_node[object_offset++] = noffset;
-
- geometry_map[geom] = pack.object_node[object_offset - 1];
-
- /* merge primitive, object and triangle indexes */
- if (bvh->pack.prim_index.size()) {
- size_t bvh_prim_index_size = bvh->pack.prim_index.size();
- int *bvh_prim_index = &bvh->pack.prim_index[0];
- int *bvh_prim_type = &bvh->pack.prim_type[0];
- uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
- uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
- float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
-
- for (size_t i = 0; i < bvh_prim_index_size; i++) {
- if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
- pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_prim_index_offset] = -1;
- }
- else {
- pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
- pack_prim_tri_verts_offset;
- }
-
- pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
- pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
- pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
- if (bvh_prim_time != NULL) {
- pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
- }
- pack_prim_index_offset++;
- }
- }
-
- /* Merge triangle vertices data. */
- if (bvh->pack.prim_tri_verts.size()) {
- const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
- memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
- &bvh->pack.prim_tri_verts[0],
- prim_tri_size * sizeof(float4));
- pack_prim_tri_verts_offset += prim_tri_size;
- }
-
- /* merge nodes */
- if (bvh->pack.leaf_nodes.size()) {
- int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
- size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
- for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
- int4 data = leaf_nodes_offset[i];
- data.x += prim_offset;
- data.y += prim_offset;
- pack_leaf_nodes[pack_leaf_nodes_offset] = data;
- for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
- pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
- }
- pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
- }
- }
-
- if (bvh->pack.nodes.size()) {
- int4 *bvh_nodes = &bvh->pack.nodes[0];
- size_t bvh_nodes_size = bvh->pack.nodes.size();
-
- for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
- size_t nsize, nsize_bbox;
- if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
- nsize = BVH_UNALIGNED_NODE_SIZE;
- nsize_bbox = 0;
- }
- else {
- nsize = BVH_NODE_SIZE;
- nsize_bbox = 0;
- }
-
- memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
-
- /* Modify offsets into arrays */
- int4 data = bvh_nodes[i + nsize_bbox];
- data.z += (data.z < 0) ? -noffset_leaf : noffset;
- data.w += (data.w < 0) ? -noffset_leaf : noffset;
- pack_nodes[pack_nodes_offset + nsize_bbox] = data;
-
- /* Usually this copies nothing, but we better
- * be prepared for possible node size extension.
- */
- memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
- &bvh_nodes[i + nsize_bbox + 1],
- sizeof(int4) * (nsize - (nsize_bbox + 1)));
-
- pack_nodes_offset += nsize;
- i += nsize;
- }
- }
-
- nodes_offset += bvh->pack.nodes.size();
- nodes_leaf_offset += bvh->pack.leaf_nodes.size();
- prim_offset += bvh->pack.prim_index.size();
- }
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h
index 033b1fd8e04..94935c26f10 100644
--- a/intern/cycles/bvh/bvh.h
+++ b/intern/cycles/bvh/bvh.h
@@ -25,17 +25,16 @@
CCL_NAMESPACE_BEGIN
-class Stats;
-class Device;
-class DeviceScene;
+class BoundBox;
class BVHNode;
-struct BVHStackEntry;
class BVHParams;
-class BoundBox;
-class LeafNode;
+class Device;
+class DeviceScene;
class Geometry;
+class LeafNode;
class Object;
class Progress;
+class Stats;
#define BVH_ALIGN 4096
#define TRI_NODE_SIZE 3
@@ -76,13 +75,10 @@ struct PackedBVH {
}
};
-enum BVH_TYPE { bvh2 };
-
/* BVH */
class BVH {
public:
- PackedBVH pack;
BVHParams params;
vector<Geometry *> geometry;
vector<Object *> objects;
@@ -90,47 +86,15 @@ class BVH {
static BVH *create(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects,
- const Device *device);
+ Device *device);
virtual ~BVH()
{
}
- virtual void build(Progress &progress, Stats *stats = NULL);
- virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/)
- {
- }
-
- void refit(Progress &progress);
-
protected:
BVH(const BVHParams &params,
const vector<Geometry *> &geometry,
const vector<Object *> &objects);
-
- /* Refit range of primitives. */
- void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
-
- /* triangles and strands */
- void pack_primitives();
- void pack_triangle(int idx, float4 storage[3]);
-
- /* merge instance BVH's */
- void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
-
- /* for subclasses to implement */
- virtual void pack_nodes(const BVHNode *root) = 0;
- virtual void refit_nodes() = 0;
-
- virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0;
-};
-
-/* Pack Utility */
-struct BVHStackEntry {
- const BVHNode *node;
- int idx;
-
- BVHStackEntry(const BVHNode *n = 0, int i = 0);
- int encodeIdx() const;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp
index c903070429e..379ae9b25ff 100644
--- a/intern/cycles/bvh/bvh2.cpp
+++ b/intern/cycles/bvh/bvh2.cpp
@@ -17,14 +17,28 @@
#include "bvh/bvh2.h"
+#include "render/hair.h"
#include "render/mesh.h"
#include "render/object.h"
+#include "bvh/bvh_build.h"
#include "bvh/bvh_node.h"
#include "bvh/bvh_unaligned.h"
+#include "util/util_foreach.h"
+#include "util/util_progress.h"
+
CCL_NAMESPACE_BEGIN
+BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i)
+{
+}
+
+int BVHStackEntry::encodeIdx() const
+{
+ return (node->is_leaf()) ? ~idx : idx;
+}
+
BVH2::BVH2(const BVHParams &params_,
const vector<Geometry *> &geometry_,
const vector<Object *> &objects_)
@@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams &params_,
{
}
+void BVH2::build(Progress &progress, Stats *)
+{
+ progress.set_substatus("Building BVH");
+
+ /* build nodes */
+ BVHBuild bvh_build(objects,
+ pack.prim_type,
+ pack.prim_index,
+ pack.prim_object,
+ pack.prim_time,
+ params,
+ progress);
+ BVHNode *bvh2_root = bvh_build.run();
+
+ if (progress.get_cancel()) {
+ if (bvh2_root != NULL) {
+ bvh2_root->deleteSubtree();
+ }
+ return;
+ }
+
+ /* BVH builder returns tree in a binary mode (with two children per inner
+ * node. Need to adopt that for a wider BVH implementations. */
+ BVHNode *root = widen_children_nodes(bvh2_root);
+ if (root != bvh2_root) {
+ bvh2_root->deleteSubtree();
+ }
+
+ if (progress.get_cancel()) {
+ if (root != NULL) {
+ root->deleteSubtree();
+ }
+ return;
+ }
+
+ /* pack triangles */
+ progress.set_substatus("Packing BVH triangles and strands");
+ pack_primitives();
+
+ if (progress.get_cancel()) {
+ root->deleteSubtree();
+ return;
+ }
+
+ /* pack nodes */
+ progress.set_substatus("Packing BVH nodes");
+ pack_nodes(root);
+
+ /* free build nodes */
+ root->deleteSubtree();
+}
+
+void BVH2::refit(Progress &progress)
+{
+ progress.set_substatus("Packing BVH primitives");
+ pack_primitives();
+
+ if (progress.get_cancel())
+ return;
+
+ progress.set_substatus("Refitting BVH nodes");
+ refit_nodes();
+}
+
BVHNode *BVH2::widen_children_nodes(const BVHNode *root)
{
return const_cast<BVHNode *>(root);
@@ -253,7 +331,7 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
const int c0 = data[0].x;
const int c1 = data[0].y;
- BVH::refit_primitives(c0, c1, bbox, visibility);
+ refit_primitives(c0, c1, bbox, visibility);
/* TODO(sergey): De-duplicate with pack_leaf(). */
float4 leaf_data[BVH_NODE_LEAF_SIZE];
@@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
}
}
+/* Refitting */
+
+void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility)
+{
+ /* Refit range of primitives. */
+ for (int prim = start; prim < end; prim++) {
+ int pidx = pack.prim_index[prim];
+ int tob = pack.prim_object[prim];
+ Object *ob = objects[tob];
+
+ if (pidx == -1) {
+ /* Object instance. */
+ bbox.grow(ob->bounds);
+ }
+ else {
+ /* Primitives. */
+ if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
+ /* Curves. */
+ const Hair *hair = static_cast<const Hair *>(ob->get_geometry());
+ int prim_offset = (params.top_level) ? hair->prim_offset : 0;
+ Hair::Curve curve = hair->get_curve(pidx - prim_offset);
+ int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
+
+ curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox);
+
+ /* Motion curves. */
+ if (hair->get_use_motion_blur()) {
+ Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if (attr) {
+ size_t hair_size = hair->get_curve_keys().size();
+ size_t steps = hair->get_motion_steps() - 1;
+ float3 *key_steps = attr->data_float3();
+
+ for (size_t i = 0; i < steps; i++)
+ curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox);
+ }
+ }
+ }
+ else {
+ /* Triangles. */
+ const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry());
+ int prim_offset = (params.top_level) ? mesh->prim_offset : 0;
+ Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset);
+ const float3 *vpos = &mesh->verts[0];
+
+ triangle.bounds_grow(vpos, bbox);
+
+ /* Motion triangles. */
+ if (mesh->use_motion_blur) {
+ Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if (attr) {
+ size_t mesh_size = mesh->verts.size();
+ size_t steps = mesh->motion_steps - 1;
+ float3 *vert_steps = attr->data_float3();
+
+ for (size_t i = 0; i < steps; i++)
+ triangle.bounds_grow(vert_steps + i * mesh_size, bbox);
+ }
+ }
+ }
+ }
+ visibility |= ob->visibility_for_tracing();
+ }
+}
+
+/* Triangles */
+
+void BVH2::pack_triangle(int idx, float4 tri_verts[3])
+{
+ int tob = pack.prim_object[idx];
+ assert(tob >= 0 && tob < objects.size());
+ const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry());
+
+ int tidx = pack.prim_index[idx];
+ Mesh::Triangle t = mesh->get_triangle(tidx);
+ const float3 *vpos = &mesh->verts[0];
+ float3 v0 = vpos[t.v[0]];
+ float3 v1 = vpos[t.v[1]];
+ float3 v2 = vpos[t.v[2]];
+
+ tri_verts[0] = float3_to_float4(v0);
+ tri_verts[1] = float3_to_float4(v1);
+ tri_verts[2] = float3_to_float4(v2);
+}
+
+void BVH2::pack_primitives()
+{
+ const size_t tidx_size = pack.prim_index.size();
+ size_t num_prim_triangles = 0;
+ /* Count number of triangles primitives in BVH. */
+ for (unsigned int i = 0; i < tidx_size; i++) {
+ if ((pack.prim_index[i] != -1)) {
+ if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
+ ++num_prim_triangles;
+ }
+ }
+ }
+ /* Reserve size for arrays. */
+ pack.prim_tri_index.clear();
+ pack.prim_tri_index.resize(tidx_size);
+ pack.prim_tri_verts.clear();
+ pack.prim_tri_verts.resize(num_prim_triangles * 3);
+ pack.prim_visibility.clear();
+ pack.prim_visibility.resize(tidx_size);
+ /* Fill in all the arrays. */
+ size_t prim_triangle_index = 0;
+ for (unsigned int i = 0; i < tidx_size; i++) {
+ if (pack.prim_index[i] != -1) {
+ int tob = pack.prim_object[i];
+ Object *ob = objects[tob];
+ if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) {
+ pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]);
+ pack.prim_tri_index[i] = 3 * prim_triangle_index;
+ ++prim_triangle_index;
+ }
+ else {
+ pack.prim_tri_index[i] = -1;
+ }
+ pack.prim_visibility[i] = ob->visibility_for_tracing();
+ }
+ else {
+ pack.prim_tri_index[i] = -1;
+ pack.prim_visibility[i] = 0;
+ }
+ }
+}
+
+/* Pack Instances */
+
+void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
+{
+ /* Adjust primitive index to point to the triangle in the global array, for
+ * geometry with transform applied and already in the top level BVH.
+ */
+ for (size_t i = 0; i < pack.prim_index.size(); i++) {
+ if (pack.prim_index[i] != -1) {
+ pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
+ }
+ }
+
+ /* track offsets of instanced BVH data in global array */
+ size_t prim_offset = pack.prim_index.size();
+ size_t nodes_offset = nodes_size;
+ size_t nodes_leaf_offset = leaf_nodes_size;
+
+ /* clear array that gives the node indexes for instanced objects */
+ pack.object_node.clear();
+
+ /* reserve */
+ size_t prim_index_size = pack.prim_index.size();
+ size_t prim_tri_verts_size = pack.prim_tri_verts.size();
+
+ size_t pack_prim_index_offset = prim_index_size;
+ size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
+ size_t pack_nodes_offset = nodes_size;
+ size_t pack_leaf_nodes_offset = leaf_nodes_size;
+ size_t object_offset = 0;
+
+ foreach (Geometry *geom, geometry) {
+ BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
+
+ if (geom->need_build_bvh(params.bvh_layout)) {
+ prim_index_size += bvh->pack.prim_index.size();
+ prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
+ nodes_size += bvh->pack.nodes.size();
+ leaf_nodes_size += bvh->pack.leaf_nodes.size();
+ }
+ }
+
+ pack.prim_index.resize(prim_index_size);
+ pack.prim_type.resize(prim_index_size);
+ pack.prim_object.resize(prim_index_size);
+ pack.prim_visibility.resize(prim_index_size);
+ pack.prim_tri_verts.resize(prim_tri_verts_size);
+ pack.prim_tri_index.resize(prim_index_size);
+ pack.nodes.resize(nodes_size);
+ pack.leaf_nodes.resize(leaf_nodes_size);
+ pack.object_node.resize(objects.size());
+
+ if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
+ pack.prim_time.resize(prim_index_size);
+ }
+
+ int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
+ int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
+ int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
+ uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL;
+ float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
+ uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
+ int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL;
+ int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL;
+ float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL;
+
+ unordered_map<Geometry *, int> geometry_map;
+
+ /* merge */
+ foreach (Object *ob, objects) {
+ Geometry *geom = ob->get_geometry();
+
+ /* We assume that if mesh doesn't need own BVH it was already included
+ * into a top-level BVH and no packing here is needed.
+ */
+ if (!geom->need_build_bvh(params.bvh_layout)) {
+ pack.object_node[object_offset++] = 0;
+ continue;
+ }
+
+ /* if mesh already added once, don't add it again, but used set
+ * node offset for this object */
+ unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom);
+
+ if (geometry_map.find(geom) != geometry_map.end()) {
+ int noffset = it->second;
+ pack.object_node[object_offset++] = noffset;
+ continue;
+ }
+
+ BVH2 *bvh = static_cast<BVH2 *>(geom->bvh);
+
+ int noffset = nodes_offset;
+ int noffset_leaf = nodes_leaf_offset;
+ int geom_prim_offset = geom->prim_offset;
+
+ /* fill in node indexes for instances */
+ if (bvh->pack.root_index == -1)
+ pack.object_node[object_offset++] = -noffset_leaf - 1;
+ else
+ pack.object_node[object_offset++] = noffset;
+
+ geometry_map[geom] = pack.object_node[object_offset - 1];
+
+ /* merge primitive, object and triangle indexes */
+ if (bvh->pack.prim_index.size()) {
+ size_t bvh_prim_index_size = bvh->pack.prim_index.size();
+ int *bvh_prim_index = &bvh->pack.prim_index[0];
+ int *bvh_prim_type = &bvh->pack.prim_type[0];
+ uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
+ uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
+ float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL;
+
+ for (size_t i = 0; i < bvh_prim_index_size; i++) {
+ if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
+ pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
+ pack_prim_tri_index[pack_prim_index_offset] = -1;
+ }
+ else {
+ pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
+ pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
+ pack_prim_tri_verts_offset;
+ }
+
+ pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
+ pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
+ pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
+ if (bvh_prim_time != NULL) {
+ pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
+ }
+ pack_prim_index_offset++;
+ }
+ }
+
+ /* Merge triangle vertices data. */
+ if (bvh->pack.prim_tri_verts.size()) {
+ const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
+ memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
+ &bvh->pack.prim_tri_verts[0],
+ prim_tri_size * sizeof(float4));
+ pack_prim_tri_verts_offset += prim_tri_size;
+ }
+
+ /* merge nodes */
+ if (bvh->pack.leaf_nodes.size()) {
+ int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
+ size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
+ for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) {
+ int4 data = leaf_nodes_offset[i];
+ data.x += prim_offset;
+ data.y += prim_offset;
+ pack_leaf_nodes[pack_leaf_nodes_offset] = data;
+ for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) {
+ pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
+ }
+ pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE;
+ }
+ }
+
+ if (bvh->pack.nodes.size()) {
+ int4 *bvh_nodes = &bvh->pack.nodes[0];
+ size_t bvh_nodes_size = bvh->pack.nodes.size();
+
+ for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
+ size_t nsize, nsize_bbox;
+ if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) {
+ nsize = BVH_UNALIGNED_NODE_SIZE;
+ nsize_bbox = 0;
+ }
+ else {
+ nsize = BVH_NODE_SIZE;
+ nsize_bbox = 0;
+ }
+
+ memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4));
+
+ /* Modify offsets into arrays */
+ int4 data = bvh_nodes[i + nsize_bbox];
+ data.z += (data.z < 0) ? -noffset_leaf : noffset;
+ data.w += (data.w < 0) ? -noffset_leaf : noffset;
+ pack_nodes[pack_nodes_offset + nsize_bbox] = data;
+
+ /* Usually this copies nothing, but we better
+ * be prepared for possible node size extension.
+ */
+ memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1],
+ &bvh_nodes[i + nsize_bbox + 1],
+ sizeof(int4) * (nsize - (nsize_bbox + 1)));
+
+ pack_nodes_offset += nsize;
+ i += nsize;
+ }
+ }
+
+ nodes_offset += bvh->pack.nodes.size();
+ nodes_leaf_offset += bvh->pack.leaf_nodes.size();
+ prim_offset += bvh->pack.prim_index.size();
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h
index fa3e45b72d2..1030a0f76c7 100644
--- a/intern/cycles/bvh/bvh2.h
+++ b/intern/cycles/bvh/bvh2.h
@@ -26,23 +26,30 @@
CCL_NAMESPACE_BEGIN
-class BVHNode;
-struct BVHStackEntry;
-class BVHParams;
-class BoundBox;
-class LeafNode;
-class Object;
-class Progress;
-
#define BVH_NODE_SIZE 4
#define BVH_NODE_LEAF_SIZE 1
#define BVH_UNALIGNED_NODE_SIZE 7
+/* Pack Utility */
+struct BVHStackEntry {
+ const BVHNode *node;
+ int idx;
+
+ BVHStackEntry(const BVHNode *n = 0, int i = 0);
+ int encodeIdx() const;
+};
+
/* BVH2
*
* Typical BVH with each node having two children.
*/
class BVH2 : public BVH {
+ public:
+ void build(Progress &progress, Stats *stats);
+ void refit(Progress &progress);
+
+ PackedBVH pack;
+
protected:
/* constructor */
friend class BVH;
@@ -51,10 +58,10 @@ class BVH2 : public BVH {
const vector<Object *> &objects);
/* Building process. */
- virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
+ virtual BVHNode *widen_children_nodes(const BVHNode *root);
/* pack */
- void pack_nodes(const BVHNode *root) override;
+ void pack_nodes(const BVHNode *root);
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf);
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1);
@@ -84,8 +91,18 @@ class BVH2 : public BVH {
uint visibility1);
/* refit */
- void refit_nodes() override;
+ void refit_nodes();
void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility);
+
+ /* Refit range of primitives. */
+ void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility);
+
+ /* triangles and strands */
+ void pack_primitives();
+ void pack_triangle(int idx, float4 storage[3]);
+
+ /* merge instance BVH's */
+ void pack_instances(size_t nodes_size, size_t leaf_nodes_size);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp
index 910e3780b2e..b874bda7186 100644
--- a/intern/cycles/bvh/bvh_embree.cpp
+++ b/intern/cycles/bvh/bvh_embree.cpp
@@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n)
return !progress->get_cancel();
}
-static size_t count_primitives(Geometry *geom)
-{
- if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
- Mesh *mesh = static_cast<Mesh *>(geom);
- return mesh->num_triangles();
- }
- else if (geom->geometry_type == Geometry::HAIR) {
- Hair *hair = static_cast<Hair *>(geom);
- return hair->num_segments();
- }
-
- return 0;
-}
-
BVHEmbree::BVHEmbree(const BVHParams &params_,
const vector<Geometry *> &geometry_,
- const vector<Object *> &objects_,
- const Device *device)
+ const vector<Object *> &objects_)
: BVH(params_, geometry_, objects_),
scene(NULL),
- mem_used(0),
- top_level(NULL),
- rtc_device((RTCDevice)device->bvh_device()),
- stats(NULL),
- curve_subdivisions(params.curve_subdivisions),
- build_quality(RTC_BUILD_QUALITY_REFIT),
- dynamic_scene(true)
+ rtc_device(NULL),
+ build_quality(RTC_BUILD_QUALITY_REFIT)
{
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
-
- rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
-
- pack.root_index = -1;
}
BVHEmbree::~BVHEmbree()
{
- if (!params.top_level) {
- destroy(scene);
- }
-}
-
-void BVHEmbree::destroy(RTCScene scene)
-{
if (scene) {
rtcReleaseScene(scene);
- scene = NULL;
- }
-}
-
-void BVHEmbree::delete_rtcScene()
-{
- if (scene) {
- /* When this BVH is used as an instance in a top level BVH, don't delete now
- * Let the top_level BVH know that it should delete it later. */
- if (top_level) {
- top_level->add_delayed_delete_scene(scene);
- }
- else {
- rtcReleaseScene(scene);
- if (delayed_delete_scenes.size()) {
- foreach (RTCScene s, delayed_delete_scenes) {
- rtcReleaseScene(s);
- }
- }
- delayed_delete_scenes.clear();
- }
- scene = NULL;
}
}
-void BVHEmbree::build(Progress &progress, Stats *stats_)
+void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
{
+ rtc_device = rtc_device_;
assert(rtc_device);
- stats = stats_;
+
+ rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats);
progress.set_substatus("Building BVH");
@@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
RTC_BUILD_QUALITY_MEDIUM);
rtcSetSceneBuildQuality(scene, build_quality);
- /* Count triangles and curves first, reserve arrays once. */
- size_t prim_count = 0;
-
- foreach (Object *ob, objects) {
- if (params.top_level) {
- if (!ob->is_traceable()) {
- continue;
- }
- if (!ob->get_geometry()->is_instanced()) {
- prim_count += count_primitives(ob->get_geometry());
- }
- else {
- ++prim_count;
- }
- }
- else {
- prim_count += count_primitives(ob->get_geometry());
- }
- }
-
- pack.prim_object.reserve(prim_count);
- pack.prim_type.reserve(prim_count);
- pack.prim_index.reserve(prim_count);
- pack.prim_tri_index.reserve(prim_count);
-
int i = 0;
-
- pack.object_node.clear();
-
foreach (Object *ob, objects) {
if (params.top_level) {
if (!ob->is_traceable()) {
@@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
}
if (progress.get_cancel()) {
- delete_rtcScene();
- stats = NULL;
return;
}
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
rtcCommitScene(scene);
-
- pack_primitives();
-
- if (progress.get_cancel()) {
- delete_rtcScene();
- stats = NULL;
- return;
- }
-
- progress.set_substatus("Packing geometry");
- pack_nodes(NULL);
-
- stats = NULL;
-}
-
-void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene)
-{
- dscene->data.bvh.scene = scene;
-}
-
-BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/)
-{
- assert(!"Must not be called.");
- return NULL;
}
void BVHEmbree::add_object(Object *ob, int i)
@@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i)
void BVHEmbree::add_instance(Object *ob, int i)
{
- if (!ob || !ob->get_geometry()) {
- assert(0);
- return;
- }
BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh);
-
- if (instance_bvh->top_level != this) {
- instance_bvh->top_level = this;
- }
+ assert(instance_bvh != NULL);
const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1;
const size_t num_motion_steps = min(num_object_motion_steps, RTC_MAX_TIME_STEP_COUNT);
@@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i)
geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm());
}
- pack.prim_index.push_back_slow(-1);
- pack.prim_object.push_back_slow(i);
- pack.prim_type.push_back_slow(PRIMITIVE_NONE);
- pack.prim_tri_index.push_back_slow(-1);
-
rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i)
void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
{
- size_t prim_offset = pack.prim_index.size();
+ size_t prim_offset = mesh->optix_prim_offset;
+
const Attribute *attr_mP = NULL;
- size_t num_geometry_motion_steps = 1;
+ size_t num_motion_steps = 1;
if (mesh->has_motion_blur()) {
attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
- num_geometry_motion_steps = mesh->get_motion_steps();
+ num_motion_steps = mesh->get_motion_steps();
}
}
- const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
- assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
+ assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
+ num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const size_t num_triangles = mesh->num_triangles();
+
RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE);
rtcSetGeometryBuildQuality(geom_id, build_quality);
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
@@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
set_tri_vertex_buffer(geom_id, mesh, false);
- size_t prim_object_size = pack.prim_object.size();
- pack.prim_object.resize(prim_object_size + num_triangles);
- size_t prim_type_size = pack.prim_type.size();
- pack.prim_type.resize(prim_type_size + num_triangles);
- size_t prim_index_size = pack.prim_index.size();
- pack.prim_index.resize(prim_index_size + num_triangles);
- pack.prim_tri_index.resize(prim_index_size + num_triangles);
- int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE);
-
- for (size_t j = 0; j < num_triangles; ++j) {
- pack.prim_object[prim_object_size + j] = i;
- pack.prim_type[prim_type_size + j] = prim_type;
- pack.prim_index[prim_index_size + j] = j;
- pack.prim_tri_index[prim_index_size + j] = j;
- }
-
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func);
rtcSetGeometryMask(geom_id, ob->visibility_for_tracing());
@@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
}
}
}
- const size_t num_verts = mesh->verts.size();
+ const size_t num_verts = mesh->get_verts().size();
for (int t = 0; t < num_motion_steps; ++t) {
const float3 *verts;
if (t == t_mid) {
- verts = &mesh->verts[0];
+ verts = mesh->get_verts().data();
}
else {
int t_ = (t > t_mid) ? (t - 1) : t;
@@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c
void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
{
- size_t prim_offset = pack.prim_index.size();
+ size_t prim_offset = hair->optix_prim_offset;
+
const Attribute *attr_mP = NULL;
- size_t num_geometry_motion_steps = 1;
+ size_t num_motion_steps = 1;
if (hair->has_motion_blur()) {
attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
if (attr_mP) {
- num_geometry_motion_steps = hair->get_motion_steps();
+ num_motion_steps = hair->get_motion_steps();
}
}
- const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT);
- const PrimitiveType primitive_type =
- (num_motion_steps > 1) ?
- ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
- PRIMITIVE_MOTION_CURVE_THICK) :
- ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
-
- assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
+ assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT);
+ num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT);
const size_t num_curves = hair->num_curves();
size_t num_segments = 0;
@@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
num_segments += c.num_segments();
}
- /* Make room for Cycles specific data. */
- size_t prim_object_size = pack.prim_object.size();
- pack.prim_object.resize(prim_object_size + num_segments);
- size_t prim_type_size = pack.prim_type.size();
- pack.prim_type.resize(prim_type_size + num_segments);
- size_t prim_index_size = pack.prim_index.size();
- pack.prim_index.resize(prim_index_size + num_segments);
- size_t prim_tri_index_size = pack.prim_index.size();
- pack.prim_tri_index.resize(prim_tri_index_size + num_segments);
-
enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ?
RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE :
RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE);
RTCGeometry geom_id = rtcNewGeometry(rtc_device, type);
- rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1);
+ rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1);
unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments);
size_t rtc_index = 0;
@@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtc_indices[rtc_index] = c.first_key + k;
/* Room for extra CVs at Catmull-Rom splines. */
rtc_indices[rtc_index] += j * 2;
- /* Cycles specific data. */
- pack.prim_object[prim_object_size + rtc_index] = i;
- pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k));
- pack.prim_index[prim_index_size + rtc_index] = j;
- pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index;
++rtc_index;
}
@@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i)
rtcReleaseGeometry(geom_id);
}
-void BVHEmbree::pack_nodes(const BVHNode *)
+void BVHEmbree::refit(Progress &progress)
{
- /* Quite a bit of this code is for compatibility with Cycles' native BVH. */
- if (!params.top_level) {
- return;
- }
-
- for (size_t i = 0; i < pack.prim_index.size(); ++i) {
- if (pack.prim_index[i] != -1) {
- pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset;
- }
- }
-
- size_t prim_offset = pack.prim_index.size();
-
- /* reserve */
- size_t prim_index_size = pack.prim_index.size();
- size_t prim_tri_verts_size = pack.prim_tri_verts.size();
-
- size_t pack_prim_index_offset = prim_index_size;
- size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
- size_t object_offset = 0;
-
- map<Geometry *, int> geometry_map;
-
- foreach (Object *ob, objects) {
- Geometry *geom = ob->get_geometry();
- BVH *bvh = geom->bvh;
-
- if (geom->need_build_bvh(BVH_LAYOUT_EMBREE)) {
- if (geometry_map.find(geom) == geometry_map.end()) {
- prim_index_size += bvh->pack.prim_index.size();
- prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
- geometry_map[geom] = 1;
- }
- }
- }
+ progress.set_substatus("Refitting BVH nodes");
- geometry_map.clear();
-
- pack.prim_index.resize(prim_index_size);
- pack.prim_type.resize(prim_index_size);
- pack.prim_object.resize(prim_index_size);
- pack.prim_visibility.clear();
- pack.prim_tri_verts.resize(prim_tri_verts_size);
- pack.prim_tri_index.resize(prim_index_size);
- pack.object_node.resize(objects.size());
-
- int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL;
- int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL;
- int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL;
- float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL;
- uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL;
-
- /* merge */
- foreach (Object *ob, objects) {
- Geometry *geom = ob->get_geometry();
-
- /* We assume that if mesh doesn't need own BVH it was already included
- * into a top-level BVH and no packing here is needed.
- */
- if (!geom->need_build_bvh(BVH_LAYOUT_EMBREE)) {
- pack.object_node[object_offset++] = prim_offset;
- continue;
- }
-
- /* if geom already added once, don't add it again, but used set
- * node offset for this object */
- map<Geometry *, int>::iterator it = geometry_map.find(geom);
-
- if (geometry_map.find(geom) != geometry_map.end()) {
- int noffset = it->second;
- pack.object_node[object_offset++] = noffset;
- continue;
- }
-
- BVHEmbree *bvh = (BVHEmbree *)geom->bvh;
-
- rtc_memory_monitor_func(stats, unaccounted_mem, true);
- unaccounted_mem = 0;
-
- int geom_prim_offset = geom->prim_offset;
-
- /* fill in node indexes for instances */
- pack.object_node[object_offset++] = prim_offset;
-
- geometry_map[geom] = pack.object_node[object_offset - 1];
-
- /* merge primitive, object and triangle indexes */
- if (bvh->pack.prim_index.size()) {
- size_t bvh_prim_index_size = bvh->pack.prim_index.size();
- int *bvh_prim_index = &bvh->pack.prim_index[0];
- int *bvh_prim_type = &bvh->pack.prim_type[0];
- uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
-
- for (size_t i = 0; i < bvh_prim_index_size; ++i) {
- if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
- pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_prim_index_offset] = -1;
- }
- else {
- pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
- pack_prim_tri_verts_offset;
- }
-
- pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
- pack_prim_object[pack_prim_index_offset] = 0;
-
- ++pack_prim_index_offset;
- }
- }
-
- /* Merge triangle vertices data. */
- if (bvh->pack.prim_tri_verts.size()) {
- const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
- memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
- &bvh->pack.prim_tri_verts[0],
- prim_tri_size * sizeof(float4));
- pack_prim_tri_verts_offset += prim_tri_size;
- }
-
- prim_offset += bvh->pack.prim_index.size();
- }
-}
-
-void BVHEmbree::refit_nodes()
-{
/* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */
unsigned geom_id = 0;
foreach (Object *ob, objects) {
@@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes()
}
geom_id += 2;
}
+
rtcCommitScene(scene);
}
+
CCL_NAMESPACE_END
#endif /* WITH_EMBREE */
diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/bvh_embree.h
index 3e895e7b588..01636fbd1dc 100644
--- a/intern/cycles/bvh/bvh_embree.h
+++ b/intern/cycles/bvh/bvh_embree.h
@@ -31,56 +31,34 @@
CCL_NAMESPACE_BEGIN
-class Geometry;
class Hair;
class Mesh;
class BVHEmbree : public BVH {
public:
- virtual void build(Progress &progress, Stats *stats) override;
- virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
- virtual ~BVHEmbree();
- RTCScene scene;
- static void destroy(RTCScene);
+ void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
+ void refit(Progress &progress);
- /* Building process. */
- virtual BVHNode *widen_children_nodes(const BVHNode *root) override;
+ RTCScene scene;
protected:
friend class BVH;
BVHEmbree(const BVHParams &params,
const vector<Geometry *> &geometry,
- const vector<Object *> &objects,
- const Device *device);
-
- virtual void pack_nodes(const BVHNode *) override;
- virtual void refit_nodes() override;
+ const vector<Object *> &objects);
+ virtual ~BVHEmbree();
void add_object(Object *ob, int i);
void add_instance(Object *ob, int i);
void add_curves(const Object *ob, const Hair *hair, int i);
void add_triangles(const Object *ob, const Mesh *mesh, int i);
- ssize_t mem_used;
-
- void add_delayed_delete_scene(RTCScene scene)
- {
- delayed_delete_scenes.push_back(scene);
- }
- BVHEmbree *top_level;
-
private:
- void delete_rtcScene();
void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update);
void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update);
RTCDevice rtc_device;
-
- Stats *stats;
- vector<RTCScene> delayed_delete_scenes;
- int curve_subdivisions;
enum RTCBuildQuality build_quality;
- bool dynamic_scene;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_multi.cpp b/intern/cycles/bvh/bvh_multi.cpp
new file mode 100644
index 00000000000..a9e771f20f1
--- /dev/null
+++ b/intern/cycles/bvh/bvh_multi.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bvh/bvh_multi.h"
+
+#include "util/util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+BVHMulti::BVHMulti(const BVHParams &params_,
+ const vector<Geometry *> &geometry_,
+ const vector<Object *> &objects_)
+ : BVH(params_, geometry_, objects_)
+{
+}
+
+BVHMulti::~BVHMulti()
+{
+ foreach (BVH *bvh, sub_bvhs) {
+ delete bvh;
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_multi.h b/intern/cycles/bvh/bvh_multi.h
new file mode 100644
index 00000000000..840438c5d0c
--- /dev/null
+++ b/intern/cycles/bvh/bvh_multi.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020, Blender Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BVH_MULTI_H__
+#define __BVH_MULTI_H__
+
+#include "bvh/bvh.h"
+#include "bvh/bvh_params.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BVHMulti : public BVH {
+ public:
+ vector<BVH *> sub_bvhs;
+
+ protected:
+ friend class BVH;
+ BVHMulti(const BVHParams &params,
+ const vector<Geometry *> &geometry,
+ const vector<Object *> &objects);
+ virtual ~BVHMulti();
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BVH_MULTI_H__ */
diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp
index 52fc9c0a50d..e094f339ede 100644
--- a/intern/cycles/bvh/bvh_optix.cpp
+++ b/intern/cycles/bvh/bvh_optix.cpp
@@ -19,212 +19,22 @@
# include "bvh/bvh_optix.h"
-# include "device/device.h"
-
-# include "render/geometry.h"
-# include "render/hair.h"
-# include "render/mesh.h"
-# include "render/object.h"
-
-# include "util/util_foreach.h"
-# include "util/util_logging.h"
-# include "util/util_progress.h"
-
CCL_NAMESPACE_BEGIN
BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Geometry *> &geometry_,
- const vector<Object *> &objects_)
- : BVH(params_, geometry_, objects_)
+ const vector<Object *> &objects_,
+ Device *device)
+ : BVH(params_, geometry_, objects_),
+ traversable_handle(0),
+ as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
+ motion_transform_data(device, "optix motion transform")
{
- optix_handle = 0;
- optix_data_handle = 0;
- do_refit = false;
}
BVHOptiX::~BVHOptiX()
{
-}
-
-void BVHOptiX::build(Progress &, Stats *)
-{
- if (params.top_level)
- pack_tlas();
- else
- pack_blas();
-}
-
-void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene)
-{
- progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure");
-
- Device *const device = dscene->bvh_nodes.device;
- if (!device->build_optix_bvh(this))
- progress.set_error("Failed to build OptiX acceleration structure");
-}
-
-void BVHOptiX::pack_blas()
-{
- // Bottom-level BVH can contain multiple primitive types, so merge them:
- assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh
- Geometry *const geom = geometry[0];
-
- if (geom->geometry_type == Geometry::HAIR) {
- Hair *const hair = static_cast<Hair *const>(geom);
- if (hair->num_curves() > 0) {
- const size_t num_curves = hair->num_curves();
- const size_t num_segments = hair->num_segments();
- pack.prim_type.reserve(pack.prim_type.size() + num_segments);
- pack.prim_index.reserve(pack.prim_index.size() + num_segments);
- pack.prim_object.reserve(pack.prim_object.size() + num_segments);
- // 'pack.prim_time' is only used in geom_curve_intersect.h
- // It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH
-
- uint type = (hair->get_use_motion_blur() &&
- hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ?
- ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
- PRIMITIVE_MOTION_CURVE_THICK) :
- ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
- PRIMITIVE_CURVE_THICK);
-
- for (size_t j = 0; j < num_curves; ++j) {
- const Hair::Curve curve = hair->get_curve(j);
- for (size_t k = 0; k < curve.num_segments(); ++k) {
- pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
- // Each curve segment points back to its curve index
- pack.prim_index.push_back_reserved(j);
- pack.prim_object.push_back_reserved(0);
- }
- }
- }
- }
- else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
- Mesh *const mesh = static_cast<Mesh *const>(geom);
- if (mesh->num_triangles() > 0) {
- const size_t num_triangles = mesh->num_triangles();
- pack.prim_type.reserve(pack.prim_type.size() + num_triangles);
- pack.prim_index.reserve(pack.prim_index.size() + num_triangles);
- pack.prim_object.reserve(pack.prim_object.size() + num_triangles);
-
- uint type = PRIMITIVE_TRIANGLE;
- if (mesh->get_use_motion_blur() && mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION))
- type = PRIMITIVE_MOTION_TRIANGLE;
-
- for (size_t k = 0; k < num_triangles; ++k) {
- pack.prim_type.push_back_reserved(type);
- pack.prim_index.push_back_reserved(k);
- pack.prim_object.push_back_reserved(0);
- }
- }
- }
-
- // Initialize visibility to zero and later update it during top-level build
- uint prev_visibility = objects[0]->get_visibility();
- objects[0]->set_visibility(0);
-
- // Update 'pack.prim_tri_index', 'pack.prim_tri_verts' and 'pack.prim_visibility'
- pack_primitives();
-
- // Reset visibility after packing
- objects[0]->set_visibility(prev_visibility);
-}
-
-void BVHOptiX::pack_tlas()
-{
- // Calculate total packed size
- size_t prim_index_size = 0;
- size_t prim_tri_verts_size = 0;
- foreach (Geometry *geom, geometry) {
- BVH *const bvh = geom->bvh;
- prim_index_size += bvh->pack.prim_index.size();
- prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
- }
-
- if (prim_index_size == 0)
- return; // Abort right away if this is an empty BVH
-
- size_t pack_offset = 0;
- size_t pack_verts_offset = 0;
-
- pack.prim_type.resize(prim_index_size);
- int *pack_prim_type = pack.prim_type.data();
- pack.prim_index.resize(prim_index_size);
- int *pack_prim_index = pack.prim_index.data();
- pack.prim_object.resize(prim_index_size);
- int *pack_prim_object = pack.prim_object.data();
- pack.prim_visibility.resize(prim_index_size);
- uint *pack_prim_visibility = pack.prim_visibility.data();
- pack.prim_tri_index.resize(prim_index_size);
- uint *pack_prim_tri_index = pack.prim_tri_index.data();
- pack.prim_tri_verts.resize(prim_tri_verts_size);
- float4 *pack_prim_tri_verts = pack.prim_tri_verts.data();
-
- // Top-level BVH should only contain instances, see 'Geometry::need_build_bvh'
- // Iterate over scene mesh list instead of objects, since the 'prim_offset' is calculated based
- // on that list, which may be ordered differently from the object list.
- foreach (Geometry *geom, geometry) {
- PackedBVH &bvh_pack = geom->bvh->pack;
- int geom_prim_offset = geom->prim_offset;
-
- // Merge visibility flags of all objects and fix object indices for non-instanced geometry
- int object_index = 0; // Unused for instanced geometry
- int object_visibility = 0;
- foreach (Object *ob, objects) {
- if (ob->get_geometry() == geom) {
- object_visibility |= ob->visibility_for_tracing();
- if (!geom->is_instanced()) {
- object_index = ob->get_device_index();
- break;
- }
- }
- }
-
- // Merge primitive, object and triangle indexes
- if (!bvh_pack.prim_index.empty()) {
- int *bvh_prim_type = &bvh_pack.prim_type[0];
- int *bvh_prim_index = &bvh_pack.prim_index[0];
- uint *bvh_prim_tri_index = &bvh_pack.prim_tri_index[0];
- uint *bvh_prim_visibility = &bvh_pack.prim_visibility[0];
-
- for (size_t i = 0; i < bvh_pack.prim_index.size(); i++, pack_offset++) {
- if (bvh_pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
- pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_offset] = -1;
- }
- else {
- pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset;
- pack_prim_tri_index[pack_offset] = bvh_prim_tri_index[i] + pack_verts_offset;
- }
-
- pack_prim_type[pack_offset] = bvh_prim_type[i];
- pack_prim_object[pack_offset] = object_index;
- pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility;
- }
- }
-
- // Merge triangle vertex data
- if (!bvh_pack.prim_tri_verts.empty()) {
- const size_t prim_tri_size = bvh_pack.prim_tri_verts.size();
- memcpy(pack_prim_tri_verts + pack_verts_offset,
- bvh_pack.prim_tri_verts.data(),
- prim_tri_size * sizeof(float4));
- pack_verts_offset += prim_tri_size;
- }
- }
-}
-
-void BVHOptiX::pack_nodes(const BVHNode *)
-{
-}
-
-void BVHOptiX::refit_nodes()
-{
- do_refit = true;
-}
-
-BVHNode *BVHOptiX::widen_children_nodes(const BVHNode *)
-{
- return NULL;
+ // Acceleration structure memory is freed via the 'as_data' destructor
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/bvh_optix.h
index 663cba67260..aa514beae0d 100644
--- a/intern/cycles/bvh/bvh_optix.h
+++ b/intern/cycles/bvh/bvh_optix.h
@@ -26,33 +26,19 @@
CCL_NAMESPACE_BEGIN
-class Geometry;
-class Optix;
-
class BVHOptiX : public BVH {
- friend class BVH;
-
public:
- uint64_t optix_handle;
- uint64_t optix_data_handle;
- bool do_refit;
+ uint64_t traversable_handle;
+ device_only_memory<char> as_data;
+ device_only_memory<char> motion_transform_data;
+ protected:
+ friend class BVH;
BVHOptiX(const BVHParams &params,
const vector<Geometry *> &geometry,
- const vector<Object *> &objects);
+ const vector<Object *> &objects,
+ Device *device);
virtual ~BVHOptiX();
-
- virtual void build(Progress &progress, Stats *) override;
- virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override;
-
- private:
- void pack_blas();
- void pack_tlas();
-
- virtual void pack_nodes(const BVHNode *) override;
- virtual void refit_nodes() override;
-
- virtual BVHNode *widen_children_nodes(const BVHNode *) override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h
index e5e3e24165d..c3271c3cfcf 100644
--- a/intern/cycles/device/cuda/device_cuda.h
+++ b/intern/cycles/device/cuda/device_cuda.h
@@ -71,6 +71,7 @@ class CUDADevice : public Device {
};
typedef map<device_memory *, CUDAMem> CUDAMemMap;
CUDAMemMap cuda_mem_map;
+ thread_mutex cuda_mem_map_mutex;
struct PixelMem {
GLuint cuPBO;
diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp
index 01adf10f252..48151365e5d 100644
--- a/intern/cycles/device/cuda/device_cuda_impl.cpp
+++ b/intern/cycles/device/cuda/device_cuda_impl.cpp
@@ -718,8 +718,10 @@ void CUDADevice::init_host_memory()
void CUDADevice::load_texture_info()
{
if (need_texture_info) {
- texture_info.copy_to_device();
+ /* Unset flag before copying, so this does not loop indefinitely if the copy below calls
+ * into 'move_textures_to_host' (which calls 'load_texture_info' again). */
need_texture_info = false;
+ texture_info.copy_to_device();
}
}
@@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem)
assert(!"mem_alloc not supported for global memory.");
}
else {
+ thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
}
}
@@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem)
tex_alloc((device_texture &)mem);
}
else {
+ thread_scoped_lock lock(cuda_mem_map_mutex);
if (!mem.device_pointer) {
generic_alloc(mem);
}
-
generic_copy_to(mem);
}
}
@@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem)
/* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory
* regardless of mem.host_pointer and mem.shared_pointer. */
+ thread_scoped_lock lock(cuda_mem_map_mutex);
if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) {
const CUDAContextScope scope(this);
cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size()));
@@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem)
tex_free((device_texture &)mem);
}
else {
+ thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size)
void CUDADevice::global_alloc(device_memory &mem)
{
if (mem.is_resident(this)) {
+ thread_scoped_lock lock(cuda_mem_map_mutex);
generic_alloc(mem);
generic_copy_to(mem);
}
@@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem)
void CUDADevice::global_free(device_memory &mem)
{
if (mem.is_resident(this) && mem.device_pointer) {
+ thread_scoped_lock lock(cuda_mem_map_mutex);
generic_free(mem);
}
}
@@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem)
size_t src_pitch = mem.data_width * dsize * mem.data_elements;
size_t dst_pitch = src_pitch;
+ thread_scoped_lock lock(cuda_mem_map_mutex);
+
if (!mem.is_resident(this)) {
cmem = &cuda_mem_map[&mem];
cmem->texobject = 0;
@@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size));
}
+ /* Unlock mutex before resizing texture info, since that may attempt to lock it again. */
+ lock.unlock();
+
/* Resize once */
const uint slot = mem.slot;
if (slot >= texture_info.size()) {
@@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem)
texDesc.filterMode = filter_mode;
texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES;
+ /* Lock again and refresh the data pointer (in case another thread modified the map in the
+ * meantime). */
+ lock.lock();
+ cmem = &cuda_mem_map[&mem];
+
cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
texture_info[slot].data = (uint64_t)cmem->texobject;
@@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem)
{
if (mem.device_pointer) {
CUDAContextScope scope(this);
+ thread_scoped_lock lock(cuda_mem_map_mutex);
const CUDAMem &cmem = cuda_mem_map[&mem];
if (cmem.texobject) {
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index eb8fb8040e3..1efd628b79b 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -17,6 +17,8 @@
#include <stdlib.h>
#include <string.h>
+#include "bvh/bvh2.h"
+
#include "device/device.h"
#include "device/device_intern.h"
@@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba,
}
}
+void Device::build_bvh(BVH *bvh, Progress &progress, bool refit)
+{
+ assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2);
+
+ BVH2 *const bvh2 = static_cast<BVH2 *>(bvh);
+ if (refit) {
+ bvh2->refit(progress);
+ }
+ else {
+ bvh2->build(progress, &stats);
+ }
+}
+
Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background)
{
#ifdef WITH_MULTI
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index 2006db02ce7..e9b7cde7a16 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -373,12 +373,6 @@ class Device {
return NULL;
}
- /* Device specific pointer for BVH creation. Currently only used by Embree. */
- virtual void *bvh_device() const
- {
- return NULL;
- }
-
/* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/)
{
@@ -427,10 +421,7 @@ class Device {
const DeviceDrawParams &draw_params);
/* acceleration structure building */
- virtual bool build_optix_bvh(BVH *)
- {
- return false;
- }
+ virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
#ifdef WITH_NETWORK
/* networking */
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index 6912ac1e638..fea4fc53d1f 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -47,6 +47,8 @@
#include "kernel/osl/osl_globals.h"
// clang-format on
+#include "bvh/bvh_embree.h"
+
#include "render/buffers.h"
#include "render/coverage.h"
@@ -188,6 +190,7 @@ class CPUDevice : public Device {
#endif
thread_spin_lock oidn_task_lock;
#ifdef WITH_EMBREE
+ RTCScene embree_scene = NULL;
RTCDevice embree_device;
#endif
@@ -472,6 +475,15 @@ class CPUDevice : public Device {
virtual void const_copy_to(const char *name, void *host, size_t size) override
{
+#if WITH_EMBREE
+ if (strcmp(name, "__data") == 0) {
+ assert(size <= sizeof(KernelData));
+
+ // Update scene handle (since it is different for each device on multi devices)
+ KernelData *const data = (KernelData *)host;
+ data->bvh.scene = embree_scene;
+ }
+#endif
kernel_const_copy(&kernel_globals, name, host, size);
}
@@ -537,13 +549,26 @@ class CPUDevice : public Device {
#endif
}
- void *bvh_device() const override
+ void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
#ifdef WITH_EMBREE
- return embree_device;
-#else
- return NULL;
+ if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE ||
+ bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) {
+ BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(bvh);
+ if (refit) {
+ bvh_embree->refit(progress);
+ }
+ else {
+ bvh_embree->build(progress, &stats, embree_device);
+ }
+
+ if (bvh->params.top_level) {
+ embree_scene = bvh_embree->scene;
+ }
+ }
+ else
#endif
+ Device::build_bvh(bvh, progress, refit);
}
void thread_run(DeviceTask &task)
diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp
index 2e72a0b4393..e5b138917ff 100644
--- a/intern/cycles/device/device_multi.cpp
+++ b/intern/cycles/device/device_multi.cpp
@@ -17,11 +17,14 @@
#include <sstream>
#include <stdlib.h>
+#include "bvh/bvh_multi.h"
+
#include "device/device.h"
#include "device/device_intern.h"
#include "device/device_network.h"
#include "render/buffers.h"
+#include "render/geometry.h"
#include "util/util_foreach.h"
#include "util/util_list.h"
@@ -141,7 +144,7 @@ class MultiDevice : public Device {
delete sub.device;
}
- const string &error_message()
+ const string &error_message() override
{
error_msg.clear();
@@ -153,7 +156,7 @@ class MultiDevice : public Device {
return error_msg;
}
- virtual bool show_samples() const
+ virtual bool show_samples() const override
{
if (devices.size() > 1) {
return false;
@@ -161,16 +164,31 @@ class MultiDevice : public Device {
return devices.front().device->show_samples();
}
- virtual BVHLayoutMask get_bvh_layout_mask() const
+ virtual BVHLayoutMask get_bvh_layout_mask() const override
{
BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL;
+ BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE;
foreach (const SubDevice &sub_device, devices) {
- bvh_layout_mask &= sub_device.device->get_bvh_layout_mask();
+ BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask();
+ bvh_layout_mask &= device_bvh_layout_mask;
+ bvh_layout_mask_all |= device_bvh_layout_mask;
+ }
+
+ /* With multiple OptiX devices, every device needs its own acceleration structure */
+ if (bvh_layout_mask == BVH_LAYOUT_OPTIX) {
+ return BVH_LAYOUT_MULTI_OPTIX;
}
+
+ /* When devices do not share a common BVH layout, fall back to creating one for each */
+ const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE);
+ if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) {
+ return BVH_LAYOUT_MULTI_OPTIX_EMBREE;
+ }
+
return bvh_layout_mask;
}
- bool load_kernels(const DeviceRequestedFeatures &requested_features)
+ bool load_kernels(const DeviceRequestedFeatures &requested_features) override
{
foreach (SubDevice &sub, devices)
if (!sub.device->load_kernels(requested_features))
@@ -188,7 +206,7 @@ class MultiDevice : public Device {
return true;
}
- bool wait_for_availability(const DeviceRequestedFeatures &requested_features)
+ bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override
{
foreach (SubDevice &sub, devices)
if (!sub.device->wait_for_availability(requested_features))
@@ -203,7 +221,7 @@ class MultiDevice : public Device {
return true;
}
- DeviceKernelStatus get_active_kernel_switch_state()
+ DeviceKernelStatus get_active_kernel_switch_state() override
{
DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL;
@@ -227,24 +245,61 @@ class MultiDevice : public Device {
return result;
}
- bool build_optix_bvh(BVH *bvh)
+ void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
+ /* Try to build and share a single acceleration structure, if possible */
+ if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) {
+ devices.back().device->build_bvh(bvh, progress, refit);
+ return;
+ }
+
+ BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh);
+ bvh_multi->sub_bvhs.resize(devices.size());
+
+ vector<BVHMulti *> geom_bvhs;
+ geom_bvhs.reserve(bvh->geometry.size());
+ foreach (Geometry *geom, bvh->geometry) {
+ geom_bvhs.push_back(static_cast<BVHMulti *>(geom->bvh));
+ }
+
/* Broadcast acceleration structure build to all render devices */
+ size_t i = 0;
foreach (SubDevice &sub, devices) {
- if (!sub.device->build_optix_bvh(bvh))
- return false;
+ /* Change geometry BVH pointers to the sub BVH */
+ for (size_t k = 0; k < bvh->geometry.size(); ++k) {
+ bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i];
+ }
+
+ if (!bvh_multi->sub_bvhs[i]) {
+ BVHParams params = bvh->params;
+ if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX)
+ params.bvh_layout = BVH_LAYOUT_OPTIX;
+ else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE)
+ params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX :
+ BVH_LAYOUT_EMBREE;
+
+ /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree
+ * (since they are put into the top level directly, see bvh_embree.cpp) */
+ if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE &&
+ !bvh->geometry[0]->is_instanced()) {
+ i++;
+ continue;
+ }
+
+ bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device);
+ }
+
+ sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit);
+ i++;
}
- return true;
- }
- virtual void *bvh_device() const
- {
- /* CPU devices will always be at the back, so simply choose the last one.
- There should only ever be one CPU device anyway and we need the Embree device for it. */
- return devices.back().device->bvh_device();
+ /* Change geomtry BVH pointers back to the multi BVH */
+ for (size_t k = 0; k < bvh->geometry.size(); ++k) {
+ bvh->geometry[k]->bvh = geom_bvhs[k];
+ }
}
- virtual void *osl_memory()
+ virtual void *osl_memory() override
{
if (devices.size() > 1) {
return NULL;
@@ -252,7 +307,7 @@ class MultiDevice : public Device {
return devices.front().device->osl_memory();
}
- bool is_resident(device_ptr key, Device *sub_device)
+ bool is_resident(device_ptr key, Device *sub_device) override
{
foreach (SubDevice &sub, devices) {
if (sub.device == sub_device) {
@@ -299,7 +354,7 @@ class MultiDevice : public Device {
return find_matching_mem_device(key, sub)->ptr_map[key];
}
- void mem_alloc(device_memory &mem)
+ void mem_alloc(device_memory &mem) override
{
device_ptr key = unique_key++;
@@ -335,7 +390,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size);
}
- void mem_copy_to(device_memory &mem)
+ void mem_copy_to(device_memory &mem) override
{
device_ptr existing_key = mem.device_pointer;
device_ptr key = (existing_key) ? existing_key : unique_key++;
@@ -378,7 +433,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size - existing_size);
}
- void mem_copy_from(device_memory &mem, int y, int w, int h, int elem)
+ void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override
{
device_ptr key = mem.device_pointer;
int i = 0, sub_h = h / devices.size();
@@ -399,7 +454,7 @@ class MultiDevice : public Device {
mem.device_pointer = key;
}
- void mem_zero(device_memory &mem)
+ void mem_zero(device_memory &mem) override
{
device_ptr existing_key = mem.device_pointer;
device_ptr key = (existing_key) ? existing_key : unique_key++;
@@ -454,7 +509,7 @@ class MultiDevice : public Device {
stats.mem_alloc(mem.device_size - existing_size);
}
- void mem_free(device_memory &mem)
+ void mem_free(device_memory &mem) override
{
device_ptr key = mem.device_pointer;
size_t existing_size = mem.device_size;
@@ -510,7 +565,7 @@ class MultiDevice : public Device {
stats.mem_free(existing_size);
}
- void const_copy_to(const char *name, void *host, size_t size)
+ void const_copy_to(const char *name, void *host, size_t size) override
{
foreach (SubDevice &sub, devices)
sub.device->const_copy_to(name, host, size);
@@ -527,7 +582,7 @@ class MultiDevice : public Device {
int dw,
int dh,
bool transparent,
- const DeviceDrawParams &draw_params)
+ const DeviceDrawParams &draw_params) override
{
assert(rgba.type == MEM_PIXELS);
@@ -551,7 +606,7 @@ class MultiDevice : public Device {
rgba.device_pointer = key;
}
- void map_tile(Device *sub_device, RenderTile &tile)
+ void map_tile(Device *sub_device, RenderTile &tile) override
{
if (!tile.buffer) {
return;
@@ -572,7 +627,7 @@ class MultiDevice : public Device {
}
}
- int device_number(Device *sub_device)
+ int device_number(Device *sub_device) override
{
int i = 0;
@@ -591,7 +646,7 @@ class MultiDevice : public Device {
return -1;
}
- void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
+ void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
{
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
RenderTile &tile = neighbors.tiles[i];
@@ -643,7 +698,7 @@ class MultiDevice : public Device {
}
}
- void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors)
+ void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override
{
RenderTile &target_tile = neighbors.target;
device_vector<float> &mem = target_tile.buffers->buffer;
@@ -677,7 +732,7 @@ class MultiDevice : public Device {
}
}
- int get_split_task_count(DeviceTask &task)
+ int get_split_task_count(DeviceTask &task) override
{
int total_tasks = 0;
list<DeviceTask> tasks;
@@ -693,7 +748,7 @@ class MultiDevice : public Device {
return total_tasks;
}
- void task_add(DeviceTask &task)
+ void task_add(DeviceTask &task) override
{
list<SubDevice> task_devices = devices;
if (!denoising_devices.empty()) {
@@ -743,7 +798,7 @@ class MultiDevice : public Device {
}
}
- void task_wait()
+ void task_wait() override
{
foreach (SubDevice &sub, devices)
sub.device->task_wait();
@@ -751,7 +806,7 @@ class MultiDevice : public Device {
sub.device->task_wait();
}
- void task_cancel()
+ void task_cancel() override
{
foreach (SubDevice &sub, devices)
sub.device->task_cancel();
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index c6276c1e955..a721f426dfe 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -31,6 +31,7 @@
# include "util/util_logging.h"
# include "util/util_md5.h"
# include "util/util_path.h"
+# include "util/util_progress.h"
# include "util/util_time.h"
# ifdef WITH_CUDA_DYNLOAD
@@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice {
bool motion_blur = false;
device_vector<SbtRecord> sbt_data;
device_only_memory<KernelParams> launch_params;
- vector<CUdeviceptr> as_mem;
OptixTraversableHandle tlas_handle = 0;
OptixDenoiser denoiser = NULL;
@@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current
const CUDAContextScope scope(cuContext);
- // Free all acceleration structures
- for (CUdeviceptr mem : as_mem) {
- cuMemFree(mem);
- }
-
sbt_data.free();
texture_info.free();
launch_params.free();
@@ -1136,11 +1131,10 @@ class OptiXDevice : public CUDADevice {
}
}
- bool build_optix_bvh(const OptixBuildInput &build_input,
- uint16_t num_motion_steps,
- OptixTraversableHandle &out_handle,
- CUdeviceptr &out_data,
- OptixBuildOperation operation)
+ bool build_optix_bvh(BVHOptiX *bvh,
+ OptixBuildOperation operation,
+ const OptixBuildInput &build_input,
+ uint16_t num_motion_steps)
{
const CUDAContextScope scope(cuContext);
@@ -1166,24 +1160,21 @@ class OptiXDevice : public CUDADevice {
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
// Allocate required output buffers
- device_only_memory<char> temp_mem(this, "temp_build_mem");
+ device_only_memory<char> temp_mem(this, "optix temp as build mem");
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
if (!temp_mem.device_pointer)
return false; // Make sure temporary memory allocation succeeded
- // Move textures to host memory if there is not enough room
- size_t size = 0, free = 0;
- cuMemGetInfo(&free, &size);
- size = sizes.outputSizeInBytes + device_working_headroom;
- if (size >= free && can_map_host) {
- move_textures_to_host(size - free, false);
- }
-
+ device_only_memory<char> &out_data = bvh->as_data;
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
- check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes));
+ assert(out_data.device == this);
+ out_data.alloc_to_device(sizes.outputSizeInBytes);
+ if (!out_data.device_pointer)
+ return false;
+ }
+ else {
+ assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes);
}
-
- as_mem.push_back(out_data);
// Finally build the acceleration structure
OptixAccelEmitDesc compacted_size_prop;
@@ -1192,6 +1183,7 @@ class OptiXDevice : public CUDADevice {
// Make sure this pointer is 8-byte aligned
compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8);
+ OptixTraversableHandle out_handle = 0;
check_result_optix_ret(optixAccelBuild(context,
NULL,
&options,
@@ -1199,11 +1191,12 @@ class OptiXDevice : public CUDADevice {
1,
temp_mem.device_pointer,
sizes.tempSizeInBytes,
- out_data,
+ out_data.device_pointer,
sizes.outputSizeInBytes,
&out_handle,
background ? &compacted_size_prop : NULL,
background ? 1 : 0));
+ bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for all operations to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
@@ -1219,81 +1212,60 @@ class OptiXDevice : public CUDADevice {
// There is no point compacting if the size does not change
if (compacted_size < sizes.outputSizeInBytes) {
- CUdeviceptr compacted_data = 0;
- if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS)
+ device_only_memory<char> compacted_data(this, "optix compacted as");
+ compacted_data.alloc_to_device(compacted_size);
+ if (!compacted_data.device_pointer)
// Do not compact if memory allocation for compacted acceleration structure fails
// Can just use the uncompacted one then, so succeed here regardless
return true;
- as_mem.push_back(compacted_data);
- check_result_optix_ret(optixAccelCompact(
- context, NULL, out_handle, compacted_data, compacted_size, &out_handle));
+ check_result_optix_ret(optixAccelCompact(context,
+ NULL,
+ out_handle,
+ compacted_data.device_pointer,
+ compacted_size,
+ &out_handle));
+ bvh->traversable_handle = static_cast<uint64_t>(out_handle);
// Wait for compaction to finish
check_result_cuda_ret(cuStreamSynchronize(NULL));
- // Free uncompacted acceleration structure
- cuMemFree(out_data);
- as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array
+ std::swap(out_data.device_size, compacted_data.device_size);
+ std::swap(out_data.device_pointer, compacted_data.device_pointer);
}
}
return true;
}
- bool build_optix_bvh(BVH *bvh) override
+ void build_bvh(BVH *bvh, Progress &progress, bool refit) override
{
- assert(bvh->params.top_level);
-
- unsigned int num_instances = 0;
- unordered_map<Geometry *, OptixTraversableHandle> geometry;
- geometry.reserve(bvh->geometry.size());
+ BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
- // Free all previous acceleration structures which can not be refit
- std::set<CUdeviceptr> refit_mem;
+ progress.set_substatus("Building OptiX acceleration structure");
- for (Geometry *geom : bvh->geometry) {
- if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) {
- refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle);
- }
- }
+ if (!bvh->params.top_level) {
+ assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1);
- for (CUdeviceptr mem : as_mem) {
- if (refit_mem.find(mem) == refit_mem.end()) {
- cuMemFree(mem);
- }
- }
-
- as_mem.clear();
-
- // Build bottom level acceleration structures (BLAS)
- // Note: Always keep this logic in sync with bvh_optix.cpp!
- for (Object *ob : bvh->objects) {
- // Skip geometry for which acceleration structure already exists
- Geometry *geom = ob->get_geometry();
- if (geometry.find(geom) != geometry.end())
- continue;
-
- OptixTraversableHandle handle;
- OptixBuildOperation operation;
- CUdeviceptr out_data;
- // Refit is only possible in viewport for now.
- if (static_cast<BVHOptiX *>(geom->bvh)->do_refit && !background) {
- out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle;
- handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle;
+ // Refit is only possible in viewport for now (because AS is built with
+ // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above)
+ OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD;
+ if (refit && !background) {
+ assert(bvh_optix->traversable_handle != 0);
operation = OPTIX_BUILD_OPERATION_UPDATE;
}
else {
- out_data = 0;
- handle = 0;
- operation = OPTIX_BUILD_OPERATION_BUILD;
+ bvh_optix->as_data.free();
+ bvh_optix->traversable_handle = 0;
}
+ // Build bottom level acceleration structures (BLAS)
+ Geometry *const geom = bvh->geometry[0];
if (geom->geometry_type == Geometry::HAIR) {
// Build BLAS for curve primitives
- Hair *const hair = static_cast<Hair *const>(ob->get_geometry());
+ Hair *const hair = static_cast<Hair *const>(geom);
if (hair->num_curves() == 0) {
- continue;
+ return;
}
const size_t num_segments = hair->num_segments();
@@ -1304,10 +1276,10 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = hair->get_motion_steps();
}
- device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY);
+ device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY);
# if OPTIX_ABI_VERSION >= 36
- device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
- device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
+ device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
+ device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
// Four control points for each curve segment
const size_t num_vertices = num_segments * 4;
if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) {
@@ -1325,7 +1297,7 @@ class OptiXDevice : public CUDADevice {
size_t center_step = (num_motion_steps - 1) / 2;
if (step != center_step) {
size_t attr_offset = (step > center_step) ? step - 1 : step;
- // Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4)
+ // Technically this is a float4 array, but sizeof(float3) == sizeof(float4)
keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size();
}
@@ -1452,22 +1424,15 @@ class OptiXDevice : public CUDADevice {
# endif
}
- // Allocate memory for new BLAS and build it
- if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
- geometry.insert({ob->get_geometry(), handle});
- static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
- static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
- static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
- }
- else {
- return false;
+ if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
+ progress.set_error("Failed to build OptiX acceleration structure");
}
}
else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
// Build BLAS for triangle primitives
- Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry());
+ Mesh *const mesh = static_cast<Mesh *const>(geom);
if (mesh->num_triangles() == 0) {
- continue;
+ return;
}
const size_t num_verts = mesh->get_verts().size();
@@ -1478,12 +1443,12 @@ class OptiXDevice : public CUDADevice {
num_motion_steps = mesh->get_motion_steps();
}
- device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY);
+ device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY);
index_data.alloc(mesh->get_triangles().size());
memcpy(index_data.data(),
mesh->get_triangles().data(),
mesh->get_triangles().size() * sizeof(int));
- device_vector<float3> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY);
+ device_vector<float3> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY);
vertex_data.alloc(num_verts * num_motion_steps);
for (size_t step = 0; step < num_motion_steps; ++step) {
@@ -1528,190 +1493,208 @@ class OptiXDevice : public CUDADevice {
build_input.triangleArray.numSbtRecords = 1;
build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset;
- // Allocate memory for new BLAS and build it
- if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) {
- geometry.insert({ob->get_geometry(), handle});
- static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data;
- static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle;
- static_cast<BVHOptiX *>(geom->bvh)->do_refit = false;
- }
- else {
- return false;
+ if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) {
+ progress.set_error("Failed to build OptiX acceleration structure");
}
}
}
+ else {
+ unsigned int num_instances = 0;
+
+ bvh_optix->as_data.free();
+ bvh_optix->traversable_handle = 0;
+ bvh_optix->motion_transform_data.free();
- // Fill instance descriptions
+ // Fill instance descriptions
# if OPTIX_ABI_VERSION < 41
- device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY);
- aabbs.alloc(bvh->objects.size());
+ device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY);
+ aabbs.alloc(bvh->objects.size());
# endif
- device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY);
- instances.alloc(bvh->objects.size());
-
- for (Object *ob : bvh->objects) {
- // Skip non-traceable objects
- if (!ob->is_traceable())
- continue;
-
- // Create separate instance for triangle/curve meshes of an object
- const auto handle_it = geometry.find(ob->get_geometry());
- if (handle_it == geometry.end()) {
- continue;
+ device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY);
+ instances.alloc(bvh->objects.size());
+
+ // Calculate total motion transform size and allocate memory for them
+ size_t motion_transform_offset = 0;
+ if (motion_blur) {
+ size_t total_motion_transform_size = 0;
+ for (Object *const ob : bvh->objects) {
+ if (ob->is_traceable() && ob->use_motion()) {
+ total_motion_transform_size = align_up(total_motion_transform_size,
+ OPTIX_TRANSFORM_BYTE_ALIGNMENT);
+ const size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
+ total_motion_transform_size = total_motion_transform_size +
+ sizeof(OptixSRTMotionTransform) +
+ motion_keys * sizeof(OptixSRTData);
+ }
+ }
+
+ assert(bvh_optix->motion_transform_data.device == this);
+ bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size);
}
- OptixTraversableHandle handle = handle_it->second;
+
+ for (Object *ob : bvh->objects) {
+ // Skip non-traceable objects
+ if (!ob->is_traceable())
+ continue;
+
+ BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh);
+ OptixTraversableHandle handle = blas->traversable_handle;
# if OPTIX_ABI_VERSION < 41
- OptixAabb &aabb = aabbs[num_instances];
- aabb.minX = ob->bounds.min.x;
- aabb.minY = ob->bounds.min.y;
- aabb.minZ = ob->bounds.min.z;
- aabb.maxX = ob->bounds.max.x;
- aabb.maxY = ob->bounds.max.y;
- aabb.maxZ = ob->bounds.max.z;
+ OptixAabb &aabb = aabbs[num_instances];
+ aabb.minX = ob->bounds.min.x;
+ aabb.minY = ob->bounds.min.y;
+ aabb.minZ = ob->bounds.min.z;
+ aabb.maxX = ob->bounds.max.x;
+ aabb.maxY = ob->bounds.max.y;
+ aabb.maxZ = ob->bounds.max.z;
# endif
- OptixInstance &instance = instances[num_instances++];
- memset(&instance, 0, sizeof(instance));
+ OptixInstance &instance = instances[num_instances++];
+ memset(&instance, 0, sizeof(instance));
- // Clear transform to identity matrix
- instance.transform[0] = 1.0f;
- instance.transform[5] = 1.0f;
- instance.transform[10] = 1.0f;
+ // Clear transform to identity matrix
+ instance.transform[0] = 1.0f;
+ instance.transform[5] = 1.0f;
+ instance.transform[10] = 1.0f;
- // Set user instance ID to object index
- instance.instanceId = ob->get_device_index();
+ // Set user instance ID to object index
+ instance.instanceId = ob->get_device_index();
- // Have to have at least one bit in the mask, or else instance would always be culled
- instance.visibilityMask = 1;
+ // Have to have at least one bit in the mask, or else instance would always be culled
+ instance.visibilityMask = 1;
- if (ob->get_geometry()->has_volume) {
- // Volumes have a special bit set in the visibility mask so a trace can mask only volumes
- instance.visibilityMask |= 2;
- }
+ if (ob->get_geometry()->has_volume) {
+ // Volumes have a special bit set in the visibility mask so a trace can mask only volumes
+ instance.visibilityMask |= 2;
+ }
- if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
- // Same applies to curves (so they can be skipped in local trace calls)
- instance.visibilityMask |= 4;
+ if (ob->get_geometry()->geometry_type == Geometry::HAIR) {
+ // Same applies to curves (so they can be skipped in local trace calls)
+ instance.visibilityMask |= 4;
# if OPTIX_ABI_VERSION >= 36
- if (motion_blur && ob->get_geometry()->has_motion_blur() &&
- DebugFlags().optix.curves_api &&
- static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
- // Select between motion blur and non-motion blur built-in intersection module
- instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
- }
+ if (motion_blur && ob->get_geometry()->has_motion_blur() &&
+ DebugFlags().optix.curves_api &&
+ static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) {
+ // Select between motion blur and non-motion blur built-in intersection module
+ instance.sbtOffset = PG_HITD_MOTION - PG_HITD;
+ }
# endif
- }
-
- // Insert motion traversable if object has motion
- if (motion_blur && ob->use_motion()) {
- size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
- size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
- motion_keys * sizeof(OptixSRTData);
-
- const CUDAContextScope scope(cuContext);
-
- CUdeviceptr motion_transform_gpu = 0;
- check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size));
- as_mem.push_back(motion_transform_gpu);
-
- // Allocate host side memory for motion transform and fill it with transform data
- OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
- new uint8_t[motion_transform_size]);
- motion_transform.child = handle;
- motion_transform.motionOptions.numKeys = ob->get_motion().size();
- motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
- motion_transform.motionOptions.timeBegin = 0.0f;
- motion_transform.motionOptions.timeEnd = 1.0f;
-
- OptixSRTData *const srt_data = motion_transform.srtData;
- array<DecomposedTransform> decomp(ob->get_motion().size());
- transform_motion_decompose(
- decomp.data(), ob->get_motion().data(), ob->get_motion().size());
-
- for (size_t i = 0; i < ob->get_motion().size(); ++i) {
- // Scale
- srt_data[i].sx = decomp[i].y.w; // scale.x.x
- srt_data[i].sy = decomp[i].z.w; // scale.y.y
- srt_data[i].sz = decomp[i].w.w; // scale.z.z
-
- // Shear
- srt_data[i].a = decomp[i].z.x; // scale.x.y
- srt_data[i].b = decomp[i].z.y; // scale.x.z
- srt_data[i].c = decomp[i].w.x; // scale.y.z
- assert(decomp[i].z.z == 0.0f); // scale.y.x
- assert(decomp[i].w.y == 0.0f); // scale.z.x
- assert(decomp[i].w.z == 0.0f); // scale.z.y
-
- // Pivot point
- srt_data[i].pvx = 0.0f;
- srt_data[i].pvy = 0.0f;
- srt_data[i].pvz = 0.0f;
-
- // Rotation
- srt_data[i].qx = decomp[i].x.x;
- srt_data[i].qy = decomp[i].x.y;
- srt_data[i].qz = decomp[i].x.z;
- srt_data[i].qw = decomp[i].x.w;
-
- // Translation
- srt_data[i].tx = decomp[i].y.x;
- srt_data[i].ty = decomp[i].y.y;
- srt_data[i].tz = decomp[i].y.z;
}
- // Upload motion transform to GPU
- cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
- delete[] reinterpret_cast<uint8_t *>(&motion_transform);
+ // Insert motion traversable if object has motion
+ if (motion_blur && ob->use_motion()) {
+ size_t motion_keys = max(ob->get_motion().size(), 2) - 2;
+ size_t motion_transform_size = sizeof(OptixSRTMotionTransform) +
+ motion_keys * sizeof(OptixSRTData);
+
+ const CUDAContextScope scope(cuContext);
+
+ motion_transform_offset = align_up(motion_transform_offset,
+ OPTIX_TRANSFORM_BYTE_ALIGNMENT);
+ CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer +
+ motion_transform_offset;
+ motion_transform_offset += motion_transform_size;
+
+ // Allocate host side memory for motion transform and fill it with transform data
+ OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>(
+ new uint8_t[motion_transform_size]);
+ motion_transform.child = handle;
+ motion_transform.motionOptions.numKeys = ob->get_motion().size();
+ motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE;
+ motion_transform.motionOptions.timeBegin = 0.0f;
+ motion_transform.motionOptions.timeEnd = 1.0f;
+
+ OptixSRTData *const srt_data = motion_transform.srtData;
+ array<DecomposedTransform> decomp(ob->get_motion().size());
+ transform_motion_decompose(
+ decomp.data(), ob->get_motion().data(), ob->get_motion().size());
+
+ for (size_t i = 0; i < ob->get_motion().size(); ++i) {
+ // Scale
+ srt_data[i].sx = decomp[i].y.w; // scale.x.x
+ srt_data[i].sy = decomp[i].z.w; // scale.y.y
+ srt_data[i].sz = decomp[i].w.w; // scale.z.z
+
+ // Shear
+ srt_data[i].a = decomp[i].z.x; // scale.x.y
+ srt_data[i].b = decomp[i].z.y; // scale.x.z
+ srt_data[i].c = decomp[i].w.x; // scale.y.z
+ assert(decomp[i].z.z == 0.0f); // scale.y.x
+ assert(decomp[i].w.y == 0.0f); // scale.z.x
+ assert(decomp[i].w.z == 0.0f); // scale.z.y
+
+ // Pivot point
+ srt_data[i].pvx = 0.0f;
+ srt_data[i].pvy = 0.0f;
+ srt_data[i].pvz = 0.0f;
+
+ // Rotation
+ srt_data[i].qx = decomp[i].x.x;
+ srt_data[i].qy = decomp[i].x.y;
+ srt_data[i].qz = decomp[i].x.z;
+ srt_data[i].qw = decomp[i].x.w;
+
+ // Translation
+ srt_data[i].tx = decomp[i].y.x;
+ srt_data[i].ty = decomp[i].y.y;
+ srt_data[i].tz = decomp[i].y.z;
+ }
- // Disable instance transform if object uses motion transform already
- instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
+ // Upload motion transform to GPU
+ cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size);
+ delete[] reinterpret_cast<uint8_t *>(&motion_transform);
- // Get traversable handle to motion transform
- optixConvertPointerToTraversableHandle(context,
- motion_transform_gpu,
- OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
- &instance.traversableHandle);
- }
- else {
- instance.traversableHandle = handle;
+ // Disable instance transform if object uses motion transform already
+ instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
- if (ob->get_geometry()->is_instanced()) {
- // Set transform matrix
- memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
+ // Get traversable handle to motion transform
+ optixConvertPointerToTraversableHandle(context,
+ motion_transform_gpu,
+ OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM,
+ &instance.traversableHandle);
}
else {
- // Disable instance transform if geometry already has it applied to vertex data
- instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
- // Non-instanced objects read ID from prim_object, so
- // distinguish them from instanced objects with high bit set
- instance.instanceId |= 0x800000;
+ instance.traversableHandle = handle;
+
+ if (ob->get_geometry()->is_instanced()) {
+ // Set transform matrix
+ memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform));
+ }
+ else {
+ // Disable instance transform if geometry already has it applied to vertex data
+ instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM;
+ // Non-instanced objects read ID from prim_object, so
+ // distinguish them from instanced objects with high bit set
+ instance.instanceId |= 0x800000;
+ }
}
}
- }
- // Upload instance descriptions
+ // Upload instance descriptions
# if OPTIX_ABI_VERSION < 41
- aabbs.resize(num_instances);
- aabbs.copy_to_device();
+ aabbs.resize(num_instances);
+ aabbs.copy_to_device();
# endif
- instances.resize(num_instances);
- instances.copy_to_device();
+ instances.resize(num_instances);
+ instances.copy_to_device();
- // Build top-level acceleration structure (TLAS)
- OptixBuildInput build_input = {};
- build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
+ // Build top-level acceleration structure (TLAS)
+ OptixBuildInput build_input = {};
+ build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2
- build_input.instanceArray.aabbs = aabbs.device_pointer;
- build_input.instanceArray.numAabbs = num_instances;
+ build_input.instanceArray.aabbs = aabbs.device_pointer;
+ build_input.instanceArray.numAabbs = num_instances;
# endif
- build_input.instanceArray.instances = instances.device_pointer;
- build_input.instanceArray.numInstances = num_instances;
+ build_input.instanceArray.instances = instances.device_pointer;
+ build_input.instanceArray.numInstances = num_instances;
- CUdeviceptr out_data = 0;
- tlas_handle = 0;
- return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD);
+ if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) {
+ progress.set_error("Failed to build OptiX acceleration structure");
+ }
+ tlas_handle = bvh_optix->traversable_handle;
+ }
}
void const_copy_to(const char *name, void *host, size_t size) override
@@ -1724,7 +1707,7 @@ class OptiXDevice : public CUDADevice {
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));
- // Fix traversable handle on multi devices
+ // Update traversable handle (since it is different for each device on multi devices)
KernelData *const data = (KernelData *)host;
*(OptixTraversableHandle *)&data->bvh.scene = tlas_handle;
diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp
index a72fbbad635..be9efcd43ee 100644
--- a/intern/cycles/device/opencl/opencl_util.cpp
+++ b/intern/cycles/device/opencl/opencl_util.cpp
@@ -780,13 +780,25 @@ bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_i
return true;
}
- /* It is possible to have Iris GPU on AMD/Apple OpenCL framework
- * (aka, it will not be on Intel framework). This isn't supported
- * and needs an explicit blacklist.
- */
- if (strstr(device_name.c_str(), "Iris")) {
+ /* Allow Intel GPUs on Intel OpenCL platform. */
+ if (platform_name.find("Intel") != string::npos) {
+ if (device_type != CL_DEVICE_TYPE_GPU) {
+ /* OpenCL on Intel CPU is not an officially supported configuration.
+ * Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */
+ return false;
+ }
+
+# ifdef __APPLE__
+ /* Apple uses own framework, which can also put Iris onto AMD frame-work.
+ * This isn't supported configuration. */
return false;
+# else
+ if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) {
+ return true;
+ }
+# endif
}
+
if (platform_name == "AMD Accelerated Parallel Processing" &&
device_type == CL_DEVICE_TYPE_GPU) {
if (driver_major < 2236) {
diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h
index ca637288bee..4605c3ea51d 100644
--- a/intern/cycles/kernel/bvh/bvh_embree.h
+++ b/intern/cycles/kernel/bvh/bvh_embree.h
@@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0]));
isect->prim = hit->primID +
- (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
- kernel_tex_fetch(__object_node, hit->instID[0] / 2);
+ (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
isect->object = hit->instID[0] / 2;
}
else {
@@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg,
RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData(
rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2));
isect->prim = hit->primID +
- (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) +
- kernel_tex_fetch(__object_node, local_object_id);
+ (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID));
isect->object = local_object_id;
isect->type = kernel_tex_fetch(__prim_type, isect->prim);
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 6beabebb92f..9d00311f746 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_BVH2 = (1 << 0),
BVH_LAYOUT_EMBREE = (1 << 1),
BVH_LAYOUT_OPTIX = (1 << 2),
+ BVH_LAYOUT_MULTI_OPTIX = (1 << 3),
+ BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4),
/* Default BVH layout to use for CPU. */
BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE,
- BVH_LAYOUT_ALL = (unsigned int)(~0u),
+ BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX,
} KernelBVHLayout;
typedef struct KernelBVH {
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 12f4eaf0b79..64b98a91853 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -15,8 +15,7 @@
*/
#include "bvh/bvh.h"
-#include "bvh/bvh_build.h"
-#include "bvh/bvh_embree.h"
+#include "bvh/bvh2.h"
#include "device/device.h"
@@ -41,6 +40,7 @@
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
+#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const
bool Geometry::need_build_bvh(BVHLayout layout) const
{
- return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX;
+ return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX ||
+ layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE;
}
bool Geometry::is_instanced() const
@@ -218,7 +219,7 @@ void Geometry::compute_bvh(
bvh->geometry = geometry;
bvh->objects = objects;
- bvh->refit(*progress);
+ device->build_bvh(bvh, *progress, true);
}
else {
progress->set_status(msg, "Building BVH");
@@ -235,7 +236,7 @@ void Geometry::compute_bvh(
delete bvh;
bvh = BVH::create(bparams, geometry, objects, device);
- MEM_GUARDED_CALL(progress, bvh->build, *progress);
+ MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false);
}
}
@@ -1162,25 +1163,66 @@ void GeometryManager::device_update_bvh(Device *device,
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
- BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
- bvh->build(progress, &device->stats);
+ delete scene->bvh;
+ BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
+ device->build_bvh(bvh, progress, false);
if (progress.get_cancel()) {
-#ifdef WITH_EMBREE
- if (dscene->data.bvh.scene) {
- BVHEmbree::destroy(dscene->data.bvh.scene);
- dscene->data.bvh.scene = NULL;
- }
-#endif
- delete bvh;
return;
}
+ PackedBVH pack;
+ if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
+ pack = std::move(static_cast<BVH2 *>(bvh)->pack);
+ }
+ else {
+ progress.set_status("Updating Scene BVH", "Packing BVH primitives");
+
+ size_t num_prims = 0;
+ size_t num_tri_verts = 0;
+ foreach (Geometry *geom, scene->geometry) {
+ if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ num_prims += mesh->num_triangles();
+ num_tri_verts += 3 * mesh->num_triangles();
+ }
+ else if (geom->is_hair()) {
+ Hair *hair = static_cast<Hair *>(geom);
+ num_prims += hair->num_segments();
+ }
+ }
+
+ pack.root_index = -1;
+ pack.prim_tri_index.reserve(num_prims);
+ pack.prim_tri_verts.reserve(num_tri_verts);
+ pack.prim_type.reserve(num_prims);
+ pack.prim_index.reserve(num_prims);
+ pack.prim_object.reserve(num_prims);
+ pack.prim_visibility.reserve(num_prims);
+
+ // Merge visibility flags of all objects and find object index for non-instanced geometry
+ unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info;
+ geometry_to_object_info.reserve(scene->geometry.size());
+ foreach (Object *ob, scene->objects) {
+ const Geometry *const geom = ob->get_geometry();
+ pair<int, uint> &info = geometry_to_object_info[geom];
+ info.second |= ob->visibility_for_tracing();
+ if (!geom->is_instanced()) {
+ info.first = ob->get_device_index();
+ }
+ }
+
+ // Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
+ // based on that list, which may be ordered differently from the object list.
+ foreach (Geometry *geom, scene->geometry) {
+ const pair<int, uint> &info = geometry_to_object_info[geom];
+ geom->pack_primitives(pack, info.first, info.second);
+ }
+ }
+
/* copy to device */
progress.set_status("Updating Scene BVH", "Copying BVH to device");
- PackedBVH &pack = bvh->pack;
-
if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device();
@@ -1226,10 +1268,8 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
-
- bvh->copy_to_device(progress, dscene);
-
- delete bvh;
+ /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
+ dscene->data.bvh.scene = NULL;
}
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
@@ -1653,14 +1693,6 @@ void GeometryManager::device_update(Device *device,
void GeometryManager::device_free(Device *device, DeviceScene *dscene)
{
-#ifdef WITH_EMBREE
- if (dscene->data.bvh.scene) {
- if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE)
- BVHEmbree::destroy(dscene->data.bvh.scene);
- dscene->data.bvh.scene = NULL;
- }
-#endif
-
dscene->bvh_nodes.free();
dscene->bvh_leaf_nodes.free();
dscene->object_node.free();
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index 1c101540464..d3daf0cc809 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -41,6 +41,7 @@ class Scene;
class SceneParams;
class Shader;
class Volume;
+struct PackedBVH;
/* Geometry
*
@@ -124,6 +125,8 @@ class Geometry : public Node {
int n,
int total);
+ virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
+
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
*
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index d67bd209142..896e798b6f9 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include "render/hair.h"
+#include "bvh/bvh.h"
+
#include "render/curves.h"
+#include "render/hair.h"
#include "render/scene.h"
CCL_NAMESPACE_BEGIN
@@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene,
}
}
+void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
+{
+ if (curve_first_key.empty())
+ return;
+
+ const size_t num_prims = num_segments();
+ pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
+ pack.prim_type.reserve(pack.prim_type.size() + num_prims);
+ pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
+ pack.prim_index.reserve(pack.prim_index.size() + num_prims);
+ pack.prim_object.reserve(pack.prim_object.size() + num_prims);
+ // 'pack.prim_time' is unused by Embree and OptiX
+
+ uint type = has_motion_blur() ?
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
+
+ for (size_t j = 0; j < num_curves(); ++j) {
+ Curve curve = get_curve(j);
+ for (size_t k = 0; k < curve.num_segments(); ++k) {
+ pack.prim_tri_index.push_back_reserved(-1);
+ pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
+ pack.prim_visibility.push_back_reserved(visibility);
+ // Each curve segment points back to its curve index
+ pack.prim_index.push_back_reserved(j + prim_offset);
+ pack.prim_object.push_back_reserved(object);
+ }
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h
index 32c5b00e879..c7be08d679c 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -145,6 +145,8 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
+
+ void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 3e6ff289c85..30858c4f68b 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -493,8 +493,8 @@ static bool image_associate_alpha(ImageManager::Image *img)
template<TypeDesc::BASETYPE FileFormat, typename StorageType>
bool ImageManager::file_load_image(Image *img, int texture_limit)
{
- /* we only handle certain number of components */
- if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) {
+ /* Ignore empty images. */
+ if (!(img->metadata.channels > 0)) {
return false;
}
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 43e664a686f..a0afdd3b841 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -805,4 +805,35 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
+void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility)
+{
+ if (triangles.empty())
+ return;
+
+ const size_t num_prims = num_triangles();
+ pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
+ pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3);
+ pack.prim_type.reserve(pack.prim_type.size() + num_prims);
+ pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
+ pack.prim_index.reserve(pack.prim_index.size() + num_prims);
+ pack.prim_object.reserve(pack.prim_object.size() + num_prims);
+ // 'pack.prim_time' is unused by Embree and OptiX
+
+ uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
+
+ for (size_t k = 0; k < num_prims; ++k) {
+ pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size());
+
+ const Mesh::Triangle t = get_triangle(k);
+ pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]]));
+ pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]]));
+ pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]]));
+
+ pack.prim_type.push_back_reserved(type);
+ pack.prim_visibility.push_back_reserved(visibility);
+ pack.prim_index.push_back_reserved(k + prim_offset);
+ pack.prim_object.push_back_reserved(object);
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index e2746e560da..b0a16fdfd8f 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -184,9 +184,8 @@ class Mesh : public Geometry {
unordered_multimap<int, int>
vert_stitching_map; /* stitching index -> multiple real vert indices */
- friend class BVH;
+ friend class BVH2;
friend class BVHBuild;
- friend class BVHEmbree;
friend class BVHSpatialSplit;
friend class DiagSplit;
friend class EdgeDice;
@@ -233,6 +232,8 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
+ void pack_primitives(PackedBVH &pack, int object, uint visibility) override;
+
void tessellate(DiagSplit *split);
SubdFace get_subd_face(size_t index) const;
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index 98c256a43b5..b7720b7aa99 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -16,6 +16,7 @@
#include <stdlib.h>
+#include "bvh/bvh.h"
#include "device/device.h"
#include "render/background.h"
#include "render/bake.h"
@@ -100,6 +101,7 @@ Scene::Scene(const SceneParams &params_, Device *device)
{
memset((void *)&dscene.data, 0, sizeof(dscene.data));
+ bvh = NULL;
camera = create_node<Camera>();
dicing_camera = create_node<Camera>();
lookup_tables = new LookupTables();
@@ -135,6 +137,9 @@ Scene::~Scene()
void Scene::free_memory(bool final)
{
+ delete bvh;
+ bvh = NULL;
+
foreach (Shader *s, shaders)
delete s;
foreach (Geometry *g, geometry)
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 6686327dc49..27e9a131bbd 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN
class AttributeRequestSet;
class Background;
+class BVH;
class Camera;
class Device;
class DeviceInfo;
@@ -220,6 +221,7 @@ class Scene : public NodeOwner {
string name;
/* data */
+ BVH *bvh;
Camera *camera;
Camera *dicing_camera;
LookupTables *lookup_tables;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index cf49dedc426..7e06b427e4d 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -347,8 +347,15 @@ void Shader::tag_update(Scene *scene)
foreach (ShaderNode *node, graph->nodes)
node->attributes(this, &attributes);
- if (has_displacement && displacement_method == DISPLACE_BOTH) {
- attributes.add(ATTR_STD_POSITION_UNDISPLACED);
+ if (has_displacement) {
+ if (displacement_method == DISPLACE_BOTH) {
+ attributes.add(ATTR_STD_POSITION_UNDISPLACED);
+ }
+ if (displacement_method_is_modified()) {
+ need_update_geometry = true;
+ scene->geometry_manager->need_update = true;
+ scene->object_manager->need_flags_update = true;
+ }
}
/* compare if the attributes changed, mesh manager will check
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index e8611839aea..1739659ab88 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -187,6 +187,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
${X11_X11_INCLUDE_PATH}
)
+ list(APPEND LIB
+ ${X11_X11_LIB}
+ ${X11_Xrender_LIB}
+ )
+
list(APPEND SRC
intern/GHOST_DisplayManagerX11.cpp
intern/GHOST_SystemX11.cpp
@@ -238,6 +243,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_xf86vmode_INCLUDE_PATH}
)
+ list(APPEND LIB
+ ${X11_Xf86vmode_LIB}
+ )
endif()
if(WITH_X11_XFIXES)
@@ -245,6 +253,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_Xfixes_INCLUDE_PATH}
)
+ list(APPEND LIB
+ ${X11_Xfixes_LIB}
+ )
endif()
if(WITH_X11_ALPHA)
@@ -256,6 +267,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
list(APPEND INC_SYS
${X11_Xinput_INCLUDE_PATH}
)
+ list(APPEND LIB
+ ${X11_Xinput_LIB}
+ )
endif()
add_definitions(-DWITH_GHOST_X11)
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 64740b68c0c..2bc73f7eb22 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -1052,7 +1052,7 @@ int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context);
/**
* Check if \a xr_context has a session that requires an upside-down frame-buffer (compared to
* OpenGL). If true, the render result should be flipped vertically for correct output.
- * \note: Only to be called after session start, may otherwise result in a false negative.
+ * \note Only to be called after session start, may otherwise result in a false negative.
*/
int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context);
diff --git a/intern/ghost/GHOST_ISystemPaths.h b/intern/ghost/GHOST_ISystemPaths.h
index b47d14984d8..e7ac752d322 100644
--- a/intern/ghost/GHOST_ISystemPaths.h
+++ b/intern/ghost/GHOST_ISystemPaths.h
@@ -78,6 +78,12 @@ class GHOST_ISystemPaths {
virtual const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const = 0;
/**
+ * Determine a special ("well known") and easy to reach user directory.
+ * \return Unsigned char string pointing to user dir (eg `~/Documents/`).
+ */
+ virtual const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
+
+ /**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir
*/
diff --git a/intern/ghost/GHOST_Path-api.h b/intern/ghost/GHOST_Path-api.h
index 4cc232be6b6..36ea70838ca 100644
--- a/intern/ghost/GHOST_Path-api.h
+++ b/intern/ghost/GHOST_Path-api.h
@@ -57,6 +57,12 @@ extern const GHOST_TUns8 *GHOST_getSystemDir(int version, const char *versionstr
extern const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr);
/**
+ * Determine a special ("well known") and easy to reach user directory.
+ * \return Unsigned char string pointing to user dir (eg `~/Documents/`).
+ */
+extern const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type);
+
+/**
* Determine the dir in which the binary file is found.
* \return Unsigned char string pointing to binary dir (eg ~/usr/local/bin/).
*/
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index a03b59d14b0..8bce064ce63 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -565,6 +565,16 @@ typedef struct {
char is_repeat;
} GHOST_TEventKeyData;
+typedef enum {
+ GHOST_kUserSpecialDirDesktop,
+ GHOST_kUserSpecialDirDocuments,
+ GHOST_kUserSpecialDirDownloads,
+ GHOST_kUserSpecialDirMusic,
+ GHOST_kUserSpecialDirPictures,
+ GHOST_kUserSpecialDirVideos,
+ /* Can be extended as needed. */
+} GHOST_TUserSpecialDirTypes;
+
typedef struct {
/** Number of pixels on a line. */
GHOST_TUns32 xPixels;
diff --git a/intern/ghost/intern/GHOST_Path-api.cpp b/intern/ghost/intern/GHOST_Path-api.cpp
index df3592fb5e5..c82e9819f3c 100644
--- a/intern/ghost/intern/GHOST_Path-api.cpp
+++ b/intern/ghost/intern/GHOST_Path-api.cpp
@@ -50,6 +50,12 @@ const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr)
return systemPaths ? systemPaths->getUserDir(version, versionstr) : NULL; /* shouldn't be NULL */
}
+const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
+{
+ GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
+ return systemPaths ? systemPaths->getUserSpecialDir(type) : NULL; /* shouldn't be NULL */
+}
+
const GHOST_TUns8 *GHOST_getBinaryDir()
{
GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get();
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index d5b8311349b..d7dbfbe8813 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -82,8 +82,8 @@ static GHOST_TButtonMask convertButton(int button)
/**
* Converts Mac rawkey codes (same for Cocoa & Carbon)
* into GHOST key codes
- * \param rawCode The raw physical key code
- * \param recvChar the character ignoring modifiers (except for shift)
+ * \param rawCode: The raw physical key code
+ * \param recvChar: the character ignoring modifiers (except for shift)
* \return Ghost key code
*/
static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction)
@@ -783,7 +783,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet
/**
* Dispose of a context.
- * \param context Pointer to the context to be disposed.
+ * \param context: Pointer to the context to be disposed.
* \return Indication of success.
*/
GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context)
@@ -1730,13 +1730,14 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
}
window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
+ NSPoint delta = [[cocoawindow contentView] convertPointToBacking:NSMakePoint(dx, dy)];
pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000,
window,
GHOST_kTrackpadEventScroll,
x,
y,
- dx,
- dy,
+ delta.x,
+ delta.y,
[event isDirectionInvertedFromDevice]));
}
} break;
diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.h b/intern/ghost/intern/GHOST_SystemPathsCocoa.h
index 188f6f02286..14633d46f03 100644
--- a/intern/ghost/intern/GHOST_SystemPathsCocoa.h
+++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.h
@@ -56,6 +56,12 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
+ * Determine a special ("well known") and easy to reach user directory.
+ * \return Unsigned char string pointing to user dir (eg `~/Documents/`).
+ */
+ const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
+
+ /**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir
*/
diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm
index 830d58ba42c..7c6184837bf 100644
--- a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm
@@ -21,6 +21,7 @@
#import <Foundation/Foundation.h>
+#include "GHOST_Debug.h"
#include "GHOST_SystemPathsCocoa.h"
#pragma mark initialization/finalization
@@ -89,6 +90,57 @@ const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserDir(int, const char *versionst
return (GHOST_TUns8 *)tempPath;
}
+const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
+{
+ static char tempPath[512] = "";
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSString *basePath;
+ NSArray *paths;
+ NSSearchPathDirectory ns_directory;
+
+ switch (type) {
+ case GHOST_kUserSpecialDirDesktop:
+ ns_directory = NSDesktopDirectory;
+ break;
+ case GHOST_kUserSpecialDirDocuments:
+ ns_directory = NSDocumentDirectory;
+ break;
+ case GHOST_kUserSpecialDirDownloads:
+ ns_directory = NSDownloadsDirectory;
+ break;
+ case GHOST_kUserSpecialDirMusic:
+ ns_directory = NSMusicDirectory;
+ break;
+ case GHOST_kUserSpecialDirPictures:
+ ns_directory = NSPicturesDirectory;
+ break;
+ case GHOST_kUserSpecialDirVideos:
+ ns_directory = NSMoviesDirectory;
+ break;
+ default:
+ GHOST_ASSERT(
+ false,
+ "GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter");
+ [pool drain];
+ return NULL;
+ }
+
+ paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES);
+
+ if ([paths count] > 0)
+ basePath = [paths objectAtIndex:0];
+ else {
+ [pool drain];
+ return NULL;
+ }
+
+ strncpy(
+ (char *)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding], sizeof(tempPath));
+
+ [pool drain];
+ return (GHOST_TUns8 *)tempPath;
+}
+
const GHOST_TUns8 *GHOST_SystemPathsCocoa::getBinaryDir() const
{
static GHOST_TUns8 tempPath[512] = "";
diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp
index ad3d490eb91..86f3a0a31fb 100644
--- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp
+++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp
@@ -21,6 +21,9 @@
* \ingroup GHOST
*/
+#include <cstdio>
+#include <sstream>
+
#include "GHOST_SystemPathsUnix.h"
#include "GHOST_Debug.h"
@@ -108,6 +111,62 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserDir(int version, const char *ve
}
}
+const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
+{
+ const char *type_str;
+
+ switch (type) {
+ case GHOST_kUserSpecialDirDesktop:
+ type_str = "DESKTOP";
+ break;
+ case GHOST_kUserSpecialDirDocuments:
+ type_str = "DOCUMENTS";
+ break;
+ case GHOST_kUserSpecialDirDownloads:
+ type_str = "DOWNLOAD";
+ break;
+ case GHOST_kUserSpecialDirMusic:
+ type_str = "MUSIC";
+ break;
+ case GHOST_kUserSpecialDirPictures:
+ type_str = "PICTURES";
+ break;
+ case GHOST_kUserSpecialDirVideos:
+ type_str = "VIDEOS";
+ break;
+ default:
+ GHOST_ASSERT(
+ false,
+ "GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter");
+ return NULL;
+ }
+
+ static string path = "";
+ /* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */
+ string command = string("xdg-user-dir ") + type_str + " 2> /dev/null";
+
+ FILE *fstream = popen(command.c_str(), "r");
+ if (fstream == NULL) {
+ return NULL;
+ }
+ std::stringstream path_stream;
+ while (!feof(fstream)) {
+ char c = fgetc(fstream);
+ /* xdg-user-dir ends the path with '\n'. */
+ if (c == '\n') {
+ break;
+ }
+ path_stream << c;
+ }
+ if (pclose(fstream) == -1) {
+ perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()");
+ return NULL;
+ }
+
+ path = path_stream.str();
+ return path[0] ? (const GHOST_TUns8 *)path.c_str() : NULL;
+}
+
const GHOST_TUns8 *GHOST_SystemPathsUnix::getBinaryDir() const
{
return NULL;
diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.h b/intern/ghost/intern/GHOST_SystemPathsUnix.h
index 8d2f26a28aa..bc9272ecd8f 100644
--- a/intern/ghost/intern/GHOST_SystemPathsUnix.h
+++ b/intern/ghost/intern/GHOST_SystemPathsUnix.h
@@ -54,6 +54,12 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
+ * Determine a special ("well known") and easy to reach user directory.
+ * \return Unsigned char string pointing to user dir (eg `~/Documents/`).
+ */
+ const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
+
+ /**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir
*/
diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp
index 673cbcad97e..193633b5c3e 100644
--- a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp
@@ -22,6 +22,7 @@
*/
#include "GHOST_SystemPathsWin32.h"
+#include "GHOST_Debug.h"
#ifndef _WIN32_IE
# define _WIN32_IE 0x0501
@@ -76,6 +77,50 @@ const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserDir(int, const char *versionst
return NULL;
}
+const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const
+{
+ GUID folderid;
+
+ switch (type) {
+ case GHOST_kUserSpecialDirDesktop:
+ folderid = FOLDERID_Desktop;
+ break;
+ case GHOST_kUserSpecialDirDocuments:
+ folderid = FOLDERID_Documents;
+ break;
+ case GHOST_kUserSpecialDirDownloads:
+ folderid = FOLDERID_Downloads;
+ break;
+ case GHOST_kUserSpecialDirMusic:
+ folderid = FOLDERID_Music;
+ break;
+ case GHOST_kUserSpecialDirPictures:
+ folderid = FOLDERID_Pictures;
+ break;
+ case GHOST_kUserSpecialDirVideos:
+ folderid = FOLDERID_Videos;
+ break;
+ default:
+ GHOST_ASSERT(
+ false,
+ "GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter");
+ return NULL;
+ }
+
+ static char knownpath[MAX_PATH * 3] = {0};
+ PWSTR knownpath_16 = NULL;
+ HRESULT hResult = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, NULL, &knownpath_16);
+
+ if (hResult == S_OK) {
+ conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
+ CoTaskMemFree(knownpath_16);
+ return (GHOST_TUns8 *)knownpath;
+ }
+
+ CoTaskMemFree(knownpath_16);
+ return NULL;
+}
+
const GHOST_TUns8 *GHOST_SystemPathsWin32::getBinaryDir() const
{
static char fullname[MAX_PATH * 3] = {0};
diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.h b/intern/ghost/intern/GHOST_SystemPathsWin32.h
index 1a1eab21bad..45e03e744a5 100644
--- a/intern/ghost/intern/GHOST_SystemPathsWin32.h
+++ b/intern/ghost/intern/GHOST_SystemPathsWin32.h
@@ -63,6 +63,12 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const;
/**
+ * Determine a special ("well known") and easy to reach user directory.
+ * \return Unsigned char string pointing to user dir (eg `~/Documents/`).
+ */
+ const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const;
+
+ /**
* Determine the directory of the current binary
* \return Unsigned char string pointing to the binary dir
*/
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 8178b9bdf1e..2bf1d0c2d35 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -422,7 +422,7 @@ finished:
/**
* Dispose of a context.
- * \param context Pointer to the context to be disposed.
+ * \param context: Pointer to the context to be disposed.
* \return Indication of success.
*/
GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context)
@@ -1614,7 +1614,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
}
case WT_PACKET:
- window->updatePendingWintabEvents();
+ window->updateWintabEventsSyncTime();
break;
////////////////////////////////////////////////////////////////////////
// Pointer events, processed
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index fe65162d168..1e1d0814d3a 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -407,7 +407,7 @@ void GHOST_WindowWayland::setOpaque() const
#endif
/**
- * \param type The type of rendering context create.
+ * \param type: The type of rendering context create.
* \return Indication of success.
*/
GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type)
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index af02663985d..a4cbf66b22d 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -1292,7 +1292,7 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
- updatePendingWintabEvents();
+ updateWintabEvents();
auto &pendingEvents = m_wintab.pendingEvents;
size_t pendingEventSize = pendingEvents.size();
@@ -1388,6 +1388,44 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
return GHOST_kSuccess;
}
+void GHOST_WindowWin32::updateWintabEvents()
+{
+ readWintabEvents();
+ // When a Wintab device is used to leave window focus, some of it's packets are periodically not
+ // queued in time to be flushed. Reading packets needs to occur before expiring packets to clear
+ // these from the queue.
+ expireWintabEvents();
+}
+
+void GHOST_WindowWin32::updateWintabEventsSyncTime()
+{
+ readWintabEvents();
+
+ if (!m_wintab.pendingEvents.empty()) {
+ auto lastEvent = m_wintab.pendingEvents.back();
+ m_wintab.sysTimeOffset = ::GetTickCount() - lastEvent.pkTime;
+ }
+
+ expireWintabEvents();
+}
+
+void GHOST_WindowWin32::readWintabEvents()
+{
+ if (!(m_wintab.packetsGet && m_wintab.context)) {
+ return;
+ }
+
+ auto &pendingEvents = m_wintab.pendingEvents;
+
+ /* Get new packets. */
+ const int numPackets = m_wintab.packetsGet(
+ m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
+
+ for (int i = 0; i < numPackets; i++) {
+ pendingEvents.push(m_wintab.pkts[i]);
+ }
+}
+
/* Wintab (per documentation but may vary with implementation) does not update when its event
* buffer is full. This is an issue because we need some synchronization point between Wintab
* events and Win32 events, so we can't drain and process the queue immediately. We need to
@@ -1396,17 +1434,12 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
* mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse
* events and no documented time stamp shared between the two, so we synchronize on mouse button
* events. */
-void GHOST_WindowWin32::updatePendingWintabEvents()
+void GHOST_WindowWin32::expireWintabEvents()
{
- if (!(m_wintab.packetsGet && m_wintab.context)) {
- return;
- }
-
auto &pendingEvents = m_wintab.pendingEvents;
- /* Clear outdated events from queue. */
- DWORD currTime = ::GetTickCount();
- DWORD millisTimeout = 500;
+ DWORD currTime = ::GetTickCount() - m_wintab.sysTimeOffset;
+ DWORD millisTimeout = 300;
while (!pendingEvents.empty()) {
DWORD pkTime = pendingEvents.front().pkTime;
@@ -1417,24 +1450,6 @@ void GHOST_WindowWin32::updatePendingWintabEvents()
break;
}
}
-
- /* Get new packets. */
- const int numPackets = m_wintab.packetsGet(
- m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
-
- int i = 0;
- /* Don't queue outdated packets, such events can include packets that occurred before the current
- * window lost and regained focus. */
- for (; i < numPackets; i++) {
- DWORD pkTime = m_wintab.pkts[i].pkTime;
-
- if (currTime < pkTime + millisTimeout) {
- break;
- }
- }
- for (; i < numPackets; i++) {
- pendingEvents.push(m_wintab.pkts[i]);
- }
}
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index a761d7d84dc..829bbdea051 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -486,9 +486,14 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
/**
- * Updates stored pending Wintab events.
+ * Updates pending Wintab events and syncs Wintab time with OS time.
*/
- void updatePendingWintabEvents();
+ void updateWintabEventsSyncTime();
+
+ /**
+ * Updates pending Wintab events.
+ */
+ void updateWintabEvents();
GHOST_TSuccess beginFullScreen() const
{
@@ -629,6 +634,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TUns8 numSysButtons = 0;
/** Cursors currently in contact mapped to system buttons */
DWORD sysButtonsPressed = 0;
+ DWORD sysTimeOffset = 0;
LONG maxPressure = 0;
LONG maxAzimuth = 0, maxAltitude = 0;
/** Reusable buffer to read in Wintab Packets. */
@@ -642,6 +648,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
*/
void initializeWintab();
+ void readWintabEvents();
+
+ void expireWintabEvents();
+
/**
* Convert Wintab system mapped (mouse) buttons into Ghost button mask.
* \param cursor: The Wintab cursor associated to the button.
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 9e40c01dffd3f720b23b906d20df8e999d34a4a
+Subproject 877a343fed9613d8e02e7fe7181d3bbb628506f
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index 7a521d41b88..b448a230da8 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -55,7 +55,6 @@ const UserDef U_default = {
.timecode_style = USER_TIMECODE_MINIMAL,
.versions = 1,
.dbl_click_time = 350,
- .wheellinescroll = 3,
.mini_axis_type = USER_MINI_AXIS_TYPE_GIZMO,
.uiflag = (USER_FILTERFILEEXTS | USER_DRAWVIEWINFO | USER_PLAINMENUS |
USER_LOCK_CURSOR_ADJUST | USER_DEPTH_CURSOR | USER_AUTOPERSP | USER_GLOBALUNDO |
diff --git a/release/license/THIRD-PARTY-LICENSES.txt b/release/license/THIRD-PARTY-LICENSES.txt
new file mode 100644
index 00000000000..3116635ba29
--- /dev/null
+++ b/release/license/THIRD-PARTY-LICENSES.txt
@@ -0,0 +1,3973 @@
+** Blosc; version 1.5.0 -- https://github.com/Blosc/c-blosc
+For Blosc - A blocking, shuffling and lossless compression library
+
+Copyright (C) 2009-2018 Francesc Alted <francesc@blosc.org>
+Copyright (C) 2019-present Blosc Development team <blosc@blosc.org>
+
+Copyright (C) 2006 by Rob Landley <rob@landley.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+------
+
+** Audaspace; version 1.3.0 -- https://audaspace.github.io/
+** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew
+** Draco; version 1.3.6 -- https://google.github.io/draco/
+** Embree; version 3.10 -- https://github.com/embree/embree
+** Mantaflow; version 0.13 -- http://mantaflow.com/
+** oneAPI Threading Building Block; version 2019_U9 --
+https://software.intel.com/en-us/oneapi/onetbb
+** OpenCL Wrangler; version 27a6867 -- https://github.com/OpenCLWrangler/clew
+** OpenImageDenoise; version 1.2.3 -- https://www.openimagedenoise.org/
+** OpenXR SDK; version 1.0.8 -- https://khronos.org/openxr
+** RangeTree; version 40ebed8aa209 -- https://github.com/ideasman42/rangetree-c
+** SDL Extension Wrangler; version 15edf8e --
+https://github.com/SDLWrangler/sdlew
+
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND
+DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction, and
+ distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by the
+ copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all other
+ entities that control, are controlled by, or are under common control
+ with that entity. For the purposes of this definition, "control" means
+ (i) the power, direct or indirect, to cause the direction or management
+ of such entity, whether by contract or otherwise, or (ii) ownership of
+ fifty percent (50%) or more of the outstanding shares, or (iii)
+ beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
+ permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation source,
+ and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but not limited
+ to compiled object code, generated documentation, and conversions to
+ other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or Object
+ form, made available under the License, as indicated by a copyright
+ notice that is included in or attached to the work (an example is
+ provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object form,
+ that is based on (or derived from) the Work and for which the editorial
+ revisions, annotations, elaborations, or other modifications represent,
+ as a whole, an original work of authorship. For the purposes of this
+ License, Derivative Works shall not include works that remain separable
+ from, or merely link (or bind by name) to the interfaces of, the Work and
+ Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including the original
+ version of the Work and any modifications or additions to that Work or
+ Derivative Works thereof, that is intentionally submitted to Licensor for
+ inclusion in the Work by the copyright owner or by an individual or Legal
+ Entity authorized to submit on behalf of the copyright owner. For the
+ purposes of this definition, "submitted" means any form of electronic,
+ verbal, or written communication sent to the Licensor or its
+ representatives, including but not limited to communication on electronic
+ mailing lists, source code control systems, and issue tracking systems
+ that are managed by, or on behalf of, the Licensor for the purpose of
+ discussing and improving the Work, but excluding communication that is
+ conspicuously marked or otherwise designated in writing by the copyright
+ owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity on
+ behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of this
+ License, each Contributor hereby grants to You a perpetual, worldwide,
+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
+ sublicense, and distribute the Work and such Derivative Works in Source or
+ Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of this
+ License, each Contributor hereby grants to You a perpetual, worldwide,
+ non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
+ this section) patent license to make, have made, use, offer to sell, sell,
+ import, and otherwise transfer the Work, where such license applies only to
+ those patent claims licensable by such Contributor that are necessarily
+ infringed by their Contribution(s) alone or by combination of their
+ Contribution(s) with the Work to which such Contribution(s) was submitted.
+ If You institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+ Contribution incorporated within the Work constitutes direct or contributory
+ patent infringement, then any patent licenses granted to You under this
+ License for that Work shall terminate as of the date such litigation is
+ filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the Work or
+ Derivative Works thereof in any medium, with or without modifications, and
+ in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a
+ copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating
+ that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You
+ distribute, all copyright, patent, trademark, and attribution notices
+ from the Source form of the Work, excluding those notices that do not
+ pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must include
+ a readable copy of the attribution notices contained within such NOTICE
+ file, excluding those notices that do not pertain to any part of the
+ Derivative Works, in at least one of the following places: within a
+ NOTICE text file distributed as part of the Derivative Works; within the
+ Source form or documentation, if provided along with the Derivative
+ Works; or, within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents of the
+ NOTICE file are for informational purposes only and do not modify the
+ License. You may add Your own attribution notices within Derivative Works
+ that You distribute, alongside or as an addendum to the NOTICE text from
+ the Work, provided that such additional attribution notices cannot be
+ construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may
+ provide additional or different license terms and conditions for use,
+ reproduction, or distribution of Your modifications, or for any such
+ Derivative Works as a whole, provided Your use, reproduction, and
+ distribution of the Work otherwise complies with the conditions stated in
+ this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any
+ Contribution intentionally submitted for inclusion in the Work by You to the
+ Licensor shall be under the terms and conditions of this License, without
+ any additional terms or conditions. Notwithstanding the above, nothing
+ herein shall supersede or modify the terms of any separate license agreement
+ you may have executed with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor, except
+ as required for reasonable and customary use in describing the origin of the
+ Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
+ writing, Licensor provides the Work (and each Contributor provides its
+ Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied, including, without limitation, any
+ warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
+ FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
+ the appropriateness of using or redistributing the Work and assume any risks
+ associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory, whether
+ in tort (including negligence), contract, or otherwise, unless required by
+ applicable law (such as deliberate and grossly negligent acts) or agreed to
+ in writing, shall any Contributor be liable to You for damages, including
+ any direct, indirect, special, incidental, or consequential damages of any
+ character arising as a result of this License or out of the use or inability
+ to use the Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all other
+ commercial damages or losses), even if such Contributor has been advised of
+ the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work
+ or Derivative Works thereof, You may choose to offer, and charge a fee for,
+ acceptance of support, warranty, indemnity, or other liability obligations
+ and/or rights consistent with this License. However, in accepting such
+ obligations, You may act only on Your own behalf and on Your sole
+ responsibility, not on behalf of any other Contributor, and only if You
+ agree to indemnify, defend, and hold each Contributor harmless for any
+ liability incurred by, or claims asserted against, such Contributor by
+ reason of your accepting any such warranty or additional liability. END OF
+ TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification
+within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+
+you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+
+distributed under the License is distributed on an "AS IS" BASIS,
+
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+
+See the License for the specific language governing permissions and
+
+limitations under the License.
+
+* For Audaspace see also this required NOTICE:
+ Copyright © 2009-2020 Jörg Müller. All rights reserved.
+* For Cuda Wrangler see also this required NOTICE:
+ Copyright 2011-2014 Blender Foundation
+* For Draco see also this required NOTICE:
+ Copyright 2018 The Draco Authors
+* For Embree see also this required NOTICE:
+ Copyright 2009-2020 Intel Corporation
+* For Mantaflow see also this required NOTICE:
+ MantaFlow fluid solver framework
+ Copyright 2011 Tobias Pfaff, Nils Thuerey
+* For oneAPI Threading Building Block see also this required NOTICE:
+ Copyright (c) 2005-2020 Intel Corporation
+* For OpenCL Wrangler see also this required NOTICE:
+ Copyright (c) 2009 Organic Vectory B.V.
+ Written by George van Venrooij
+* For OpenImageDenoise see also this required NOTICE:
+ Copyright 2009-2020 Intel Corporation
+* For OpenXR SDK see also this required NOTICE:
+ Copyright (c) 2017-2020 The Khronos Group Inc.
+ Copyright (c) 2017-2019 Valve Corporation
+ Copyright (c) 2017-2019 LunarG, Inc.
+ Copyright (c) 2019 Collabora, Ltd.
+* For RangeTree see also this required NOTICE:
+ Copyright (c) 2016, Campbell Barton.
+* For SDL Extension Wrangler see also this required NOTICE:
+ Copyright 2014 Blender Foundation
+
+------
+
+** OpenJPEG; version 2.3.1 -- https://github.com/uclouvain/openjpeg
+Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+Copyright (c) 2002-2014, Professor Benoit Macq
+Copyright (c) 2003-2014, Antonin Descampe
+Copyright (c) 2003-2009, Francois-Olivier Devaux
+Copyright (c) 2005, Herve Drolon, FreeImage Team
+Copyright (c) 2002-2003, Yannick Verschueren
+Copyright (c) 2001-2003, David Janssens
+Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
+Copyright (c) 2012, CS Systemes d'Information, France
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------
+
+** Ceres Solver; version 2.0.0 -- http://ceres-solver.org/
+Ceres Solver - A fast non-linear least squares minimizer
+Copyright 2016 Google Inc. All rights reserved.
+** Curve-Fit-nD; version ddcd5bd -- https://github.com/ideasman42/curve-fit-nd
+Copyright (c) 2016, Blender Foundation.
+** Google C++ Testing Framework; version 1.10.0 --
+https://github.com/google/googletest
+Copyright 2007, Google Inc.
+All rights reserved.
+** Google Flags; version 2.2.1 -- https://github.com/gflags/gflags
+Copyright (c) 1999, Google Inc.
+All rights reserved.
+** Google Logging; version 4.4.0 -- https://github.com/google/glog
+Copyright (c) 2006, Google Inc.
+All rights reserved.
+** Open Shading Language; version 1.10.10 --
+https://github.com/imageworks/OpenShadingLanguage
+Copyright Contributors to the Open Shading Language project.
+** OpenColorIO; version 1.1.1 --
+https://github.com/AcademySoftwareFoundation/OpenColorIO
+Copyright Contributors to the OpenColorIO Project.
+** OpenEXR; version 2.4.0 --
+https://github.com/AcademySoftwareFoundation/openexr
+Copyright Contributors to the OpenEXR Project. All rights reserved.
+** OpenImageIO; version 2.1.15.0 -- http://www.openimageio.org
+Copyright (c) 2008-present by Contributors to the OpenImageIO project. All
+Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ 3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------
+
+** AutoPackage; version 1.0 -- http://autopackage.org
+BinReloc - a library for creating relocatable executables
+Written by: Hongli Lai <h.lai@chello.nl>
+** LZMA SDK; version 4.65 -- https://www.7-zip.org/sdk.html
+LZMA SDK: Public Domain
+
+Creative Commons Legal Code
+
+CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT
+PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN
+"AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS
+DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS
+LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE
+INFORMATION OR WORKS PROVIDED HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific works
+("Commons") that the public can reliably and without fear of later claims of
+infringement build upon, modify, incorporate in other works, reuse and
+redistribute as freely as possible in any form whatsoever and for any purposes,
+including without limitation commercial purposes. These owners may contribute
+to the Commons to promote the ideal of a free culture and the further
+production of creative, cultural and scientific works, or to gain reputation or
+greater distribution for their Work in part through the use and efforts of
+others.
+
+For these and/or other purposes and motivations, and without any expectation of
+additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and
+publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
+ protected by copyright and related or neighboring rights ("Copyright and
+ Related Rights"). Copyright and Related Rights include, but are not limited
+ to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+
+ ii. moral rights retained by the original author(s) and/or performer(s);
+
+ iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation thereof,
+ including any amended or successor version of such directive); and
+
+ vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national implementations
+ thereof.
+
+ 2. Waiver. To the greatest extent permitted by, but not in contravention of,
+ applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+ unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+ and Related Rights and associated claims and causes of action, whether now
+ known or unknown (including existing as well as future claims and causes of
+ action), in the Work (i) in all territories worldwide, (ii) for the maximum
+ duration provided by applicable law or treaty (including future time
+ extensions), (iii) in any current or future medium and for any number of
+ copies, and (iv) for any purpose whatsoever, including without limitation
+ commercial, advertising or promotional purposes (the "Waiver"). Affirmer
+ makes the Waiver for the benefit of each member of the public at large and
+ to the detriment of Affirmer's heirs and successors, fully intending that
+ such Waiver shall not be subject to revocation, rescission, cancellation,
+ termination, or any other legal or equitable action to disrupt the quiet
+ enjoyment of the Work by the public as contemplated by Affirmer's express
+ Statement of Purpose.
+
+ 3. Public License Fallback. Should any part of the Waiver for any reason be
+ judged legally invalid or ineffective under applicable law, then the Waiver
+ shall be preserved to the maximum extent permitted taking into account
+ Affirmer's express Statement of Purpose. In addition, to the extent the
+ Waiver is so judged Affirmer hereby grants to each affected person a
+ royalty-free, non transferable, non sublicensable, non exclusive,
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
+ maximum duration provided by applicable law or treaty (including future time
+ extensions), (iii) in any current or future medium and for any number of
+ copies, and (iv) for any purpose whatsoever, including without limitation
+ commercial, advertising or promotional purposes (the "License"). The License
+ shall be deemed effective as of the date CC0 was applied by Affirmer to the
+ Work. Should any part of the License for any reason be judged legally
+ invalid or ineffective under applicable law, such partial invalidity or
+ ineffectiveness shall not invalidate the remainder of the License, and in
+ such case Affirmer hereby affirms that he or she will not (i) exercise any
+ of his or her remaining Copyright and Related Rights in the Work or (ii)
+ assert any associated claims and causes of action with respect to the Work,
+ in either case contrary to Affirmer's express Statement of Purpose.
+
+ 4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied, statutory
+ or otherwise, including without limitation warranties of title,
+ merchantability, fitness for a particular purpose, non infringement, or
+ the absence of latent or other defects, accuracy, or the present or
+ absence of errors, whether or not discoverable, all to the greatest
+ extent permissible under applicable law.
+
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the Work.
+
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to this
+ CC0 or use of the Work.
+
+------
+
+** Potrace; version 1.16 -- http://potrace.sourceforge.net/
+Copyright (C) 2001-2019 Peter Selinger.
+
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains a notice
+ placed by the copyright holder saying it may be distributed under the terms
+ of this General Public License. The "Program", below, refers to any such
+ program or work, and a "work based on the Program" means either the Program
+ or any derivative work under copyright law: that is to say, a work
+ containing the Program or a portion of it, either verbatim or with
+ modifications and/or translated into another language. (Hereinafter,
+ translation is included without limitation in the term "modification".) Each
+ licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not covered
+ by this License; they are outside its scope. The act of running the Program
+ is not restricted, and the output from the Program is covered only if its
+ contents constitute a work based on the Program (independent of having been
+ made by running the Program). Whether that is true depends on what the
+ Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's source code
+ as you receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice and
+ disclaimer of warranty; keep intact all the notices that refer to this
+ License and to the absence of any warranty; and give any other recipients of
+ the Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion of it,
+ thus forming a work based on the Program, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that you
+ also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a
+ copy of this License. (Exception: if the Program itself is interactive
+ but does not normally print such an announcement, your work based on the
+ Program is not required to print an announcement.)
+
+ These requirements apply to the modified work as a whole. If identifiable
+ sections of that work are not derived from the Program, and can be
+ reasonably considered independent and separate works in themselves, then
+ this License, and its terms, do not apply to those sections when you
+ distribute them as separate works. But when you distribute the same sections
+ as part of a whole which is a work based on the Program, the distribution of
+ the whole must be on the terms of this License, whose permissions for other
+ licensees extend to the entire whole, and thus to each and every part
+ regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest your
+ rights to work written entirely by you; rather, the intent is to exercise
+ the right to control the distribution of derivative or collective works
+ based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program with
+ the Program (or with a work based on the Program) on a volume of a storage
+ or distribution medium does not bring the other work under the scope of this
+ License.
+
+ 3. You may copy and distribute the Program (or a work based on it, under
+ Section 2) in object code or executable form under the terms of Sections 1
+ and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+ The source code for a work means the preferred form of the work for making
+ modifications to it. For an executable work, complete source code means all
+ the source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the executable. However, as a special exception, the source
+ code distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler, kernel,
+ and so on) of the operating system on which the executable runs, unless that
+ component itself accompanies the executable.
+
+ If distribution of executable or object code is made by offering access to
+ copy from a designated place, then offering equivalent access to copy the
+ source code from the same place counts as distribution of the source code,
+ even though third parties are not compelled to copy the source along with
+ the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program except as
+ expressly provided under this License. Any attempt otherwise to copy,
+ modify, sublicense or distribute the Program is void, and will automatically
+ terminate your rights under this License. However, parties who have received
+ copies, or rights, from you under this License will not have their licenses
+ terminated so long as such parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute the
+ Program or its derivative works. These actions are prohibited by law if you
+ do not accept this License. Therefore, by modifying or distributing the
+ Program (or any work based on the Program), you indicate your acceptance of
+ this License to do so, and all its terms and conditions for copying,
+ distributing or modifying the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the original
+ licensor to copy, distribute or modify the Program subject to these terms
+ and conditions. You may not impose any further restrictions on the
+ recipients' exercise of the rights granted herein. You are not responsible
+ for enforcing compliance by third parties to this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute so
+ as to satisfy simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not distribute
+ the Program at all. For example, if a patent license would not permit
+ royalty-free redistribution of the Program by all those who receive copies
+ directly or indirectly through you, then the only way you could satisfy both
+ it and this License would be to refrain entirely from distribution of the
+ Program.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply and
+ the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any patents
+ or other property right claims or to contest validity of any such claims;
+ this section has the sole purpose of protecting the integrity of the free
+ software distribution system, which is implemented by public license
+ practices. Many people have made generous contributions to the wide range of
+ software distributed through that system in reliance on consistent
+ application of that system; it is up to the author/donor to decide if he or
+ she is willing to distribute software through any other system and a
+ licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be a
+ consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Program under this License may add an
+ explicit geographical distribution limitation excluding those countries, so
+ that distribution is permitted only in or among countries not thus excluded.
+ In such case, this License incorporates the limitation as if written in the
+ body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions of
+ the General Public License from time to time. Such new versions will be
+ similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Program does not specify a version number of
+ this License, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free programs
+ whose distribution conditions are different, write to the author to ask for
+ permission. For software which is copyrighted by the Free Software
+ Foundation, write to the Free Software Foundation; we sometimes make
+ exceptions for this. Our decision will be guided by the two goals of
+ preserving the free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+ THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
+ THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
+ PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+ CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
+ LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
+ THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+<one line to give the program's name and an idea of what it does.>
+
+Copyright (C) <yyyy> <name of author>
+
+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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
+and you are welcome to redistribute it under certain conditions; type `show c'
+for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here is
+a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice
+
+------
+
+** FFTW; version 3.3.8 -- http://www.fftw.org/
+Copyright (c) 2003, 2007-14 Matteo Frigo
+Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
+** GMP; version 6.2.0 -- https://gmplib.org/
+Copyright 1996-2020 Free Software Foundation, Inc.
+** OpenAL; version 1.20.1 -- http://openal-soft.org
+Copyright (c) 2015, Archontis Politis
+Copyright (c) 2019, Christopher Robinson
+
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains a notice
+ placed by the copyright holder saying it may be distributed under the terms
+ of this General Public License. The "Program", below, refers to any such
+ program or work, and a "work based on the Program" means either the Program
+ or any derivative work under copyright law: that is to say, a work
+ containing the Program or a portion of it, either verbatim or with
+ modifications and/or translated into another language. (Hereinafter,
+ translation is included without limitation in the term "modification".) Each
+ licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not covered
+ by this License; they are outside its scope. The act of running the Program
+ is not restricted, and the output from the Program is covered only if its
+ contents constitute a work based on the Program (independent of having been
+ made by running the Program). Whether that is true depends on what the
+ Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's source code
+ as you receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice and
+ disclaimer of warranty; keep intact all the notices that refer to this
+ License and to the absence of any warranty; and give any other recipients of
+ the Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion of it,
+ thus forming a work based on the Program, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that you
+ also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a
+ copy of this License. (Exception: if the Program itself is interactive
+ but does not normally print such an announcement, your work based on the
+ Program is not required to print an announcement.)
+
+ These requirements apply to the modified work as a whole. If identifiable
+ sections of that work are not derived from the Program, and can be
+ reasonably considered independent and separate works in themselves, then
+ this License, and its terms, do not apply to those sections when you
+ distribute them as separate works. But when you distribute the same sections
+ as part of a whole which is a work based on the Program, the distribution of
+ the whole must be on the terms of this License, whose permissions for other
+ licensees extend to the entire whole, and thus to each and every part
+ regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest your
+ rights to work written entirely by you; rather, the intent is to exercise
+ the right to control the distribution of derivative or collective works
+ based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program with
+ the Program (or with a work based on the Program) on a volume of a storage
+ or distribution medium does not bring the other work under the scope of this
+ License.
+
+ 3. You may copy and distribute the Program (or a work based on it, under
+ Section 2) in object code or executable form under the terms of Sections 1
+ and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+ The source code for a work means the preferred form of the work for making
+ modifications to it. For an executable work, complete source code means all
+ the source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the executable. However, as a special exception, the source
+ code distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler, kernel,
+ and so on) of the operating system on which the executable runs, unless that
+ component itself accompanies the executable.
+
+ If distribution of executable or object code is made by offering access to
+ copy from a designated place, then offering equivalent access to copy the
+ source code from the same place counts as distribution of the source code,
+ even though third parties are not compelled to copy the source along with
+ the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program except as
+ expressly provided under this License. Any attempt otherwise to copy,
+ modify, sublicense or distribute the Program is void, and will automatically
+ terminate your rights under this License. However, parties who have received
+ copies, or rights, from you under this License will not have their licenses
+ terminated so long as such parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute the
+ Program or its derivative works. These actions are prohibited by law if you
+ do not accept this License. Therefore, by modifying or distributing the
+ Program (or any work based on the Program), you indicate your acceptance of
+ this License to do so, and all its terms and conditions for copying,
+ distributing or modifying the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the original
+ licensor to copy, distribute or modify the Program subject to these terms
+ and conditions. You may not impose any further restrictions on the
+ recipients' exercise of the rights granted herein. You are not responsible
+ for enforcing compliance by third parties to this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute so
+ as to satisfy simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not distribute
+ the Program at all. For example, if a patent license would not permit
+ royalty-free redistribution of the Program by all those who receive copies
+ directly or indirectly through you, then the only way you could satisfy both
+ it and this License would be to refrain entirely from distribution of the
+ Program.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply and
+ the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any patents
+ or other property right claims or to contest validity of any such claims;
+ this section has the sole purpose of protecting the integrity of the free
+ software distribution system, which is implemented by public license
+ practices. Many people have made generous contributions to the wide range of
+ software distributed through that system in reliance on consistent
+ application of that system; it is up to the author/donor to decide if he or
+ she is willing to distribute software through any other system and a
+ licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be a
+ consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Program under this License may add an
+ explicit geographical distribution limitation excluding those countries, so
+ that distribution is permitted only in or among countries not thus excluded.
+ In such case, this License incorporates the limitation as if written in the
+ body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions of
+ the General Public License from time to time. Such new versions will be
+ similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Program does not specify a version number of
+ this License, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free programs
+ whose distribution conditions are different, write to the author to ask for
+ permission. For software which is copyrighted by the Free Software
+ Foundation, write to the Free Software Foundation; we sometimes make
+ exceptions for this. Our decision will be guided by the two goals of
+ preserving the free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+ THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
+ THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
+ PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+ CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
+ LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
+ THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+< one line to give the program's name and an idea of what it does. >
+
+Copyright (C) < yyyy > < name of author >
+
+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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
+and you are welcome to redistribute it under certain conditions; type `show c'
+for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here is
+a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice This General
+Public License does not permit incorporating your program into proprietary
+programs. If your program is a subroutine library, you may consider it more
+useful to permit linking proprietary applications with the library. If this is
+what you want to do, use the GNU Lesser General Public License instead of this
+License.
+
+------
+
+** miniLZO; version 2.08 -- http://www.oberhumer.com/opensource/lzo/
+LZO and miniLZO are Copyright (C) 1996-2014 Markus Franz Xaver Oberhumer
+All Rights Reserved.
+** The FreeType Project; version 2.10.2 --
+https://sourceforge.net/projects/freetype
+Copyright (C) 1996-2020 by David Turner, Robert Wilhelm, and Werner Lemberg.
+** X Drag and Drop; version 2000-08-08 --
+https://freedesktop.org/wiki/Specifications/XDND/
+xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
+Copyright (C) 1996-2000 Paul Sheer
+
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains a notice
+ placed by the copyright holder saying it may be distributed under the terms
+ of this General Public License. The "Program", below, refers to any such
+ program or work, and a "work based on the Program" means either the Program
+ or any derivative work under copyright law: that is to say, a work
+ containing the Program or a portion of it, either verbatim or with
+ modifications and/or translated into another language. (Hereinafter,
+ translation is included without limitation in the term "modification".) Each
+ licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not covered
+ by this License; they are outside its scope. The act of running the Program
+ is not restricted, and the output from the Program is covered only if its
+ contents constitute a work based on the Program (independent of having been
+ made by running the Program). Whether that is true depends on what the
+ Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's source code
+ as you receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice and
+ disclaimer of warranty; keep intact all the notices that refer to this
+ License and to the absence of any warranty; and give any other recipients of
+ the Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion of it,
+ thus forming a work based on the Program, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that you
+ also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof,
+ to be licensed as a whole at no charge to all third parties under the
+ terms of this License.
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in
+ the most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a
+ copy of this License. (Exception: if the Program itself is interactive
+ but does not normally print such an announcement, your work based on the
+ Program is not required to print an announcement.)
+
+ These requirements apply to the modified work as a whole. If identifiable
+ sections of that work are not derived from the Program, and can be
+ reasonably considered independent and separate works in themselves, then
+ this License, and its terms, do not apply to those sections when you
+ distribute them as separate works. But when you distribute the same sections
+ as part of a whole which is a work based on the Program, the distribution of
+ the whole must be on the terms of this License, whose permissions for other
+ licensees extend to the entire whole, and thus to each and every part
+ regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest your
+ rights to work written entirely by you; rather, the intent is to exercise
+ the right to control the distribution of derivative or collective works
+ based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program with
+ the Program (or with a work based on the Program) on a volume of a storage
+ or distribution medium does not bring the other work under the scope of this
+ License.
+
+ 3. You may copy and distribute the Program (or a work based on it, under
+ Section 2) in object code or executable form under the terms of Sections 1
+ and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+ The source code for a work means the preferred form of the work for making
+ modifications to it. For an executable work, complete source code means all
+ the source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the executable. However, as a special exception, the source
+ code distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler, kernel,
+ and so on) of the operating system on which the executable runs, unless that
+ component itself accompanies the executable.
+
+ If distribution of executable or object code is made by offering access to
+ copy from a designated place, then offering equivalent access to copy the
+ source code from the same place counts as distribution of the source code,
+ even though third parties are not compelled to copy the source along with
+ the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program except as
+ expressly provided under this License. Any attempt otherwise to copy,
+ modify, sublicense or distribute the Program is void, and will automatically
+ terminate your rights under this License. However, parties who have received
+ copies, or rights, from you under this License will not have their licenses
+ terminated so long as such parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute the
+ Program or its derivative works. These actions are prohibited by law if you
+ do not accept this License. Therefore, by modifying or distributing the
+ Program (or any work based on the Program), you indicate your acceptance of
+ this License to do so, and all its terms and conditions for copying,
+ distributing or modifying the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the original
+ licensor to copy, distribute or modify the Program subject to these terms
+ and conditions. You may not impose any further restrictions on the
+ recipients' exercise of the rights granted herein. You are not responsible
+ for enforcing compliance by third parties to this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute so
+ as to satisfy simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not distribute
+ the Program at all. For example, if a patent license would not permit
+ royalty-free redistribution of the Program by all those who receive copies
+ directly or indirectly through you, then the only way you could satisfy both
+ it and this License would be to refrain entirely from distribution of the
+ Program.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply and
+ the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any patents
+ or other property right claims or to contest validity of any such claims;
+ this section has the sole purpose of protecting the integrity of the free
+ software distribution system, which is implemented by public license
+ practices. Many people have made generous contributions to the wide range of
+ software distributed through that system in reliance on consistent
+ application of that system; it is up to the author/donor to decide if he or
+ she is willing to distribute software through any other system and a
+ licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be a
+ consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Program under this License may add an
+ explicit geographical distribution limitation excluding those countries, so
+ that distribution is permitted only in or among countries not thus excluded.
+ In such case, this License incorporates the limitation as if written in the
+ body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions of
+ the General Public License from time to time. Such new versions will be
+ similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Program does not specify a version number of
+ this License, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free programs
+ whose distribution conditions are different, write to the author to ask for
+ permission. For software which is copyrighted by the Free Software
+ Foundation, write to the Free Software Foundation; we sometimes make
+ exceptions for this. Our decision will be guided by the two goals of
+ preserving the free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+ THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
+ THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
+ PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+ CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
+ LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
+ THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+<one line to give the program's name and an idea of what it does.>
+
+Copyright (C) <yyyy> <name of author>
+
+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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
+and you are welcome to redistribute it under certain conditions; type `show c'
+for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here is
+a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice This General
+Public License does not permit incorporating your program into proprietary
+programs. If your program is a subroutine library, you may consider it more
+useful to permit linking proprietary applications with the library. If this is
+what you want to do, use the GNU Lesser General Public License instead of this
+License.
+
+------
+
+** Eigen, template library for linear algebra: matrices, vectors, numerical
+solvers, and related algorithms; version 3.2.7 --
+http://eigen.tuxfamily.org/index.php?title=Main_Page
+This file is part of Eigen, a lightweight C++ template library for linear
+algebra.
+** Free Spacenav; version 0.2.3 --
+http://downloads.sourceforge.net/project/spacenav
+Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+GNU GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <http s ://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The GNU General Public License is a free, copyleft license for software and
+other kinds of works.
+
+The licenses for most software and other practical works are designed to take
+away your freedom to share and change the works. By contrast, the GNU General
+Public License is intended to guarantee your freedom to share and change all
+versions of a program--to make sure it remains free software for all its users.
+We, the Free Software Foundation, use the GNU General Public License for most
+of our software; it applies also to any other work released this way by its
+authors. You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for them if you wish), that you
+receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs, and that you know you can do
+these things.
+
+To protect your rights, we need to prevent others from denying you these rights
+or asking you to surrender the rights. Therefore, you have certain
+responsibilities if you distribute copies of the software, or if you modify it:
+responsibilities to respect the freedom of others.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must pass on to the recipients the same freedoms that you received.
+You must make sure that they, too, receive or can get the source code. And you
+must show them these terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps: (1) assert
+copyright on the software, and (2) offer you this License giving you legal
+permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains that
+there is no warranty for this free software. For both users' and authors' sake,
+the GPL requires that modified versions be marked as changed, so that their
+problems will not be attributed erroneously to authors of previous versions.
+
+Some devices are designed to deny users access to install or run modified
+versions of the software inside them, although the manufacturer can do so. This
+is fundamentally incompatible with the aim of protecting users' freedom to
+change the software. The systematic pattern of such abuse occurs in the area of
+products for individuals to use, which is precisely where it is most
+unacceptable. Therefore, we have designed this version of the GPL to prohibit
+the practice for those products. If such problems arise substantially in other
+domains, we stand ready to extend this provision to those domains in future
+versions of the GPL, as needed to protect the freedom of users.
+
+Finally, every program is threatened constantly by software patents. States
+should not allow patents to restrict development and use of software on
+general-purpose computers, but in those that do, we wish to avoid the special
+danger that patents applied to a free program could make it effectively
+proprietary. To prevent this, the GPL assures that patents cannot be used to
+render the program non-free.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+ works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this License.
+ Each licensee is addressed as "you". "Licensees" and "recipients" may be
+ individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work in a
+ fashion requiring copyright permission, other than the making of an exact
+ copy. The resulting work is called a "modified version" of the earlier work
+ or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based on the
+ Program.
+
+ To "propagate" a work means to do anything with it that, without permission,
+ would make you directly or secondarily liable for infringement under
+ applicable copyright law, except executing it on a computer or modifying a
+ private copy. Propagation includes copying, distribution (with or without
+ modification), making available to the public, and in some countries other
+ activities as well.
+
+ To "convey" a work means any kind of propagation that enables other parties
+ to make or receive copies. Mere interaction with a user through a computer
+ network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices" to the
+ extent that it includes a convenient and prominently visible feature that
+ (1) displays an appropriate copyright notice, and (2) tells the user that
+ there is no warranty for the work (except to the extent that warranties are
+ provided), that licensees may convey the work under this License, and how to
+ view a copy of this License. If the interface presents a list of user
+ commands or options, such as a menu, a prominent item in the list meets this
+ criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work for making
+ modifications to it. "Object code" means any non-source form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+ standard defined by a recognized standards body, or, in the case of
+ interfaces specified for a particular programming language, one that is
+ widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other than
+ the work as a whole, that (a) is included in the normal form of packaging a
+ Major Component, but which is not part of that Major Component, and (b)
+ serves only to enable use of the work with that Major Component, or to
+ implement a Standard Interface for which an implementation is available to
+ the public in source code form. A "Major Component", in this context, means
+ a major essential component (kernel, window system, and so on) of the
+ specific operating system (if any) on which the executable work runs, or a
+ compiler used to produce the work, or an object code interpreter used to run
+ it.
+
+ The "Corresponding Source" for a work in object code form means all the
+ source code needed to generate, install, and (for an executable work) run
+ the object code and to modify the work, including scripts to control those
+ activities. However, it does not include the work's System Libraries, or
+ general-purpose tools or generally available free programs which are used
+ unmodified in performing those activities but which are not part of the
+ work. For example, Corresponding Source includes interface definition files
+ associated with source files for the work, and the source code for shared
+ libraries and dynamically linked subprograms that the work is specifically
+ designed to require, such as by intimate data communication or control flow
+ between those subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users can regenerate
+ automatically from other parts of the Corresponding Source.
+
+ The Corresponding Source for a work in source code form is that same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of copyright
+ on the Program, and are irrevocable provided the stated conditions are met.
+ This License explicitly affirms your unlimited permission to run the
+ unmodified Program. The output from running a covered work is covered by
+ this License only if the output, given its content, constitutes a covered
+ work. This License acknowledges your rights of fair use or other equivalent,
+ as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not convey,
+ without conditions so long as your license otherwise remains in force. You
+ may convey covered works to others for the sole purpose of having them make
+ modifications exclusively for you, or provide you with facilities for
+ running those works, provided that you comply with the terms of this License
+ in conveying all material for which you do not control copyright. Those thus
+ making or running the covered works for you must do so exclusively on your
+ behalf, under your direction and control, on terms that prohibit them from
+ making any copies of your copyrighted material outside their relationship
+ with you.
+
+ Conveying under any other circumstances is permitted solely under the
+ conditions stated below. Sublicensing is not allowed; section 10 makes it
+ unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological measure
+ under any applicable law fulfilling obligations under article 11 of the WIPO
+ copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
+ restricting circumvention of such measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+ circumvention of technological measures to the extent such circumvention is
+ effected by exercising rights under this License with respect to the covered
+ work, and you disclaim any intention to limit operation or modification of
+ the work as a means of enforcing, against the work's users, your or third
+ parties' legal rights to forbid circumvention of technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you receive
+ it, in any medium, provided that you conspicuously and appropriately publish
+ on each copy an appropriate copyright notice; keep intact all notices
+ stating that this License and any non-permissive terms added in accord with
+ section 7 apply to the code; keep intact all notices of the absence of any
+ warranty; and give all recipients a copy of this License along with the
+ Program.
+
+ You may charge any price or no price for each copy that you convey, and you
+ may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to produce
+ it from the Program, in the form of source code under the terms of section
+ 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified it,
+ and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is released
+ under this License and any conditions added under section 7. This
+ requirement modifies the requirement in section 4 to "keep intact all
+ notices".
+
+ c) You must license the entire work, as a whole, under this License to
+ anyone who comes into possession of a copy. This License will therefore
+ apply, along with any applicable section 7 additional terms, to the whole
+ of the work, and all its parts, regardless of how they are packaged. This
+ License gives no permission to license the work in any other way, but it
+ does not invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your work need
+ not make them do so.
+
+ A compilation of a covered work with other separate and independent works,
+ which are not by their nature extensions of the covered work, and which are
+ not combined with it such as to form a larger program, in or on a volume of
+ a storage or distribution medium, is called an "aggregate" if the
+ compilation and its resulting copyright are not used to limit the access or
+ legal rights of the compilation's users beyond what the individual works
+ permit. Inclusion of a covered work in an aggregate does not cause this
+ License to apply to the other parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms of
+ sections 4 and 5, provided that you also convey the machine-readable
+ Corresponding Source under the terms of this License, in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium customarily used
+ for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a written
+ offer, valid for at least three years and valid for as long as you offer
+ spare parts or customer support for that product model, to give anyone
+ who possesses the object code either (1) a copy of the Corresponding
+ Source for all the software in the product that is covered by this
+ License, on a durable physical medium customarily used for software
+ interchange, for a price no more than your reasonable cost of physically
+ performing this conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the written
+ offer to provide the Corresponding Source. This alternative is allowed
+ only occasionally and noncommercially, and only if you received the
+ object code with such an offer, in accord with subsection 6b.
+
+ d) Convey the object code by offering access from a designated place
+ (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no further
+ charge. You need not require recipients to copy the Corresponding Source
+ along with the object code. If the place to copy the object code is a
+ network server, the Corresponding Source may be on a different server
+ (operated by you or a third party) that supports equivalent copying
+ facilities, provided you maintain clear directions next to the object
+ code saying where to find the Corresponding Source. Regardless of what
+ server hosts the Corresponding Source, you remain obligated to ensure
+ that it is available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided you
+ inform other peers where the object code and Corresponding Source of the
+ work are being offered to the general public at no charge under
+ subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded from
+ the Corresponding Source as a System Library, need not be included in
+ conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+ tangible personal property which is normally used for personal, family, or
+ household purposes, or (2) anything designed or sold for incorporation into
+ a dwelling. In determining whether a product is a consumer product, doubtful
+ cases shall be resolved in favor of coverage. For a particular product
+ received by a particular user, "normally used" refers to a typical or common
+ use of that class of product, regardless of the status of the particular
+ user or of the way in which the particular user actually uses, or expects or
+ is expected to use, the product. A product is a consumer product regardless
+ of whether the product has substantial commercial, industrial or
+ non-consumer uses, unless such uses represent the only significant mode of
+ use of the product.
+
+ "Installation Information" for a User Product means any methods, procedures,
+ authorization keys, or other information required to install and execute
+ modified versions of a covered work in that User Product from a modified
+ version of its Corresponding Source. The information must suffice to ensure
+ that the continued functioning of the modified object code is in no case
+ prevented or interfered with solely because modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+ specifically for use in, a User Product, and the conveying occurs as part of
+ a transaction in which the right of possession and use of the User Product
+ is transferred to the recipient in perpetuity or for a fixed term
+ (regardless of how the transaction is characterized), the Corresponding
+ Source conveyed under this section must be accompanied by the Installation
+ Information. But this requirement does not apply if neither you nor any
+ third party retains the ability to install modified object code on the User
+ Product (for example, the work has been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+ requirement to continue to provide support service, warranty, or updates for
+ a work that has been modified or installed by the recipient, or for the User
+ Product in which it has been modified or installed. Access to a network may
+ be denied when the modification itself materially and adversely affects the
+ operation of the network or violates the rules and protocols for
+ communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided, in
+ accord with this section must be in a format that is publicly documented
+ (and with an implementation available to the public in source code form),
+ and must require no special password or key for unpacking, reading or
+ copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this License
+ by making exceptions from one or more of its conditions. Additional
+ permissions that are applicable to the entire Program shall be treated as
+ though they were included in this License, to the extent that they are valid
+ under applicable law. If additional permissions apply only to part of the
+ Program, that part may be used separately under those permissions, but the
+ entire Program remains governed by this License without regard to the
+ additional permissions.
+
+ When you convey a copy of a covered work, you may at your option remove any
+ additional permissions from that copy, or from any part of it. (Additional
+ permissions may be written to require their own removal in certain cases
+ when you modify the work.) You may place additional permissions on material,
+ added by you to a covered work, for which you have or can give appropriate
+ copyright permission.
+
+ Notwithstanding any other provision of this License, for material you add to
+ a covered work, you may (if authorized by the copyright holders of that
+ material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the terms
+ of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or author
+ attributions in that material or in the Appropriate Legal Notices
+ displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in reasonable
+ ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some trade
+ names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that material by
+ anyone who conveys the material (or modified versions of it) with
+ contractual assumptions of liability to the recipient, for any liability
+ that these contractual assumptions directly impose on those licensors and
+ authors.
+
+ All other non-permissive additional terms are considered "further
+ restrictions" within the meaning of section 10. If the Program as you
+ received it, or any part of it, contains a notice stating that it is
+ governed by this License along with a term that is a further restriction,
+ you may remove that term. If a license document contains a further
+ restriction but permits relicensing or conveying under this License, you may
+ add to a covered work material governed by the terms of that license
+ document, provided that the further restriction does not survive such
+ relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you must
+ place, in the relevant source files, a statement of the additional terms
+ that apply to those files, or a notice indicating where to find the
+ applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the form of
+ a separately written license, or stated as exceptions; the above
+ requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly provided
+ under this License. Any attempt otherwise to propagate or modify it is void,
+ and will automatically terminate your rights under this License (including
+ any patent licenses granted under the third paragraph of section 11).
+
+ However, if you cease all violation of this License, then your license from
+ a particular copyright holder is reinstated (a) provisionally, unless and
+ until the copyright holder explicitly and finally terminates your license,
+ and (b) permanently, if the copyright holder fails to notify you of the
+ violation by some reasonable means prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is reinstated
+ permanently if the copyright holder notifies you of the violation by some
+ reasonable means, this is the first time you have received notice of
+ violation of this License (for any work) from that copyright holder, and you
+ cure the violation prior to 30 days after your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+ licenses of parties who have received copies or rights from you under this
+ License. If your rights have been terminated and not permanently reinstated,
+ you do not qualify to receive new licenses for the same material under
+ section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or run a
+ copy of the Program. Ancillary propagation of a covered work occurring
+ solely as a consequence of using peer-to-peer transmission to receive a copy
+ likewise does not require acceptance. However, nothing other than this
+ License grants you permission to propagate or modify any covered work. These
+ actions infringe copyright if you do not accept this License. Therefore, by
+ modifying or propagating a covered work, you indicate your acceptance of
+ this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically receives a
+ license from the original licensors, to run, modify and propagate that work,
+ subject to this License. You are not responsible for enforcing compliance by
+ third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+ organization, or substantially all assets of one, or subdividing an
+ organization, or merging organizations. If propagation of a covered work
+ results from an entity transaction, each party to that transaction who
+ receives a copy of the work also receives whatever licenses to the work the
+ party's predecessor in interest had or could give under the previous
+ paragraph, plus a right to possession of the Corresponding Source of the
+ work from the predecessor in interest, if the predecessor has it or can get
+ it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the rights
+ granted or affirmed under this License. For example, you may not impose a
+ license fee, royalty, or other charge for exercise of rights granted under
+ this License, and you may not initiate litigation (including a cross-claim
+ or counterclaim in a lawsuit) alleging that any patent claim is infringed by
+ making, using, selling, offering for sale, or importing the Program or any
+ portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this License
+ of the Program or a work on which the Program is based. The work thus
+ licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims owned or
+ controlled by the contributor, whether already acquired or hereafter
+ acquired, that would be infringed by some manner, permitted by this License,
+ of making, using, or selling its contributor version, but do not include
+ claims that would be infringed only as a consequence of further modification
+ of the contributor version. For purposes of this definition, "control"
+ includes the right to grant patent sublicenses in a manner consistent with
+ the requirements of this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free patent
+ license under the contributor's essential patent claims, to make, use, sell,
+ offer for sale, import and otherwise run, modify and propagate the contents
+ of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+ agreement or commitment, however denominated, not to enforce a patent (such
+ as an express permission to practice a patent or covenant not to sue for
+ patent infringement). To "grant" such a patent license to a party means to
+ make such an agreement or commitment not to enforce a patent against the
+ party.
+
+ If you convey a covered work, knowingly relying on a patent license, and the
+ Corresponding Source of the work is not available for anyone to copy, free
+ of charge and under the terms of this License, through a publicly available
+ network server or other readily accessible means, then you must either (1)
+ cause the Corresponding Source to be so available, or (2) arrange to deprive
+ yourself of the benefit of the patent license for this particular work, or
+ (3) arrange, in a manner consistent with the requirements of this License,
+ to extend the patent license to downstream recipients. "Knowingly relying"
+ means you have actual knowledge that, but for the patent license, your
+ conveying the covered work in a country, or your recipient's use of the
+ covered work in a country, would infringe one or more identifiable patents
+ in that country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or arrangement,
+ you convey, or propagate by procuring conveyance of, a covered work, and
+ grant a patent license to some of the parties receiving the covered work
+ authorizing them to use, propagate, modify or convey a specific copy of the
+ covered work, then the patent license you grant is automatically extended to
+ all recipients of the covered work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within the scope
+ of its coverage, prohibits the exercise of, or is conditioned on the
+ non-exercise of one or more of the rights that are specifically granted
+ under this License. You may not convey a covered work if you are a party to
+ an arrangement with a third party that is in the business of distributing
+ software, under which you make payment to the third party based on the
+ extent of your activity of conveying the work, and under which the third
+ party grants, to any of the parties who would receive the covered work from
+ you, a discriminatory patent license (a) in connection with copies of the
+ covered work conveyed by you (or copies made from those copies), or (b)
+ primarily for and in connection with specific products or compilations that
+ contain the covered work, unless you entered into that arrangement, or that
+ patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting any
+ implied license or other defenses to infringement that may otherwise be
+ available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot convey a
+ covered work so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you may
+ not convey it at all. For example, if you agree to terms that obligate you
+ to collect a royalty for further conveying from those to whom you convey the
+ Program, the only way you could satisfy both those terms and this License
+ would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have permission to
+ link or combine any covered work with a work licensed under version 3 of the
+ GNU Affero General Public License into a single combined work, and to convey
+ the resulting work. The terms of this License will continue to apply to the
+ part which is the covered work, but the special requirements of the GNU
+ Affero General Public License, section 13, concerning interaction through a
+ network will apply to the combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of the
+ GNU General Public License from time to time. Such new versions will be
+ similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies that a certain numbered version of the GNU General Public License
+ "or any later version" applies to it, you have the option of following the
+ terms and conditions either of that numbered version or of any later version
+ published by the Free Software Foundation. If the Program does not specify a
+ version number of the GNU General Public License, you may choose any version
+ ever published by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future versions of
+ the GNU General Public License can be used, that proxy's public statement of
+ acceptance of a version permanently authorizes you to choose that version
+ for the Program.
+
+ Later license versions may give you additional or different permissions.
+ However, no additional obligations are imposed on any author or copyright
+ holder as a result of your choosing to follow a later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+ LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+ OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+ EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.
+ SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
+ SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+ ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE
+ PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
+ OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
+ OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided above
+ cannot be given local legal effect according to their terms, reviewing
+ courts shall apply local law that most closely approximates an absolute
+ waiver of all civil liability in connection with the Program, unless a
+ warranty or assumption of liability accompanies a copy of the Program in
+ return for a fee. END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively state the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+
+Copyright (C) <year> <name of author>
+
+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 3 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, see <http s ://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short notice like
+this when it starts in an interactive mode:
+
+<program> Copyright (C) <year> <name of author>
+
+This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+
+This is free software, and you are welcome to redistribute it under certain
+conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands might
+be different; for a GUI interface, you would use an "about box".
+
+You should also get your employer (if you work as a programmer) or school, if
+any, to sign a "copyright disclaimer" for the program, if necessary. For more
+information on this, and how to apply and follow the GNU GPL, see <http s
+://www.gnu.org/licenses/>.
+
+The GNU General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may consider
+it more useful to permit linking proprietary applications with the library. If
+this is what you want to do, use the GNU Lesser General Public License instead
+of this License. But first, please read <http s ://www.gnu.org/ licenses
+/why-not-lgpl.html>.
+
+------
+
+** FFmpeg; version 4.2.3 -- http://ffmpeg.org/
+-
+
+GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts as the
+successor of the GNU Library Public License, version 2, hence the version
+number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public Licenses are intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially
+designated software packages--typically libraries--of the Free Software
+Foundation and other authors who decide to use it. You can use it too, but we
+suggest you first think carefully about whether this license or the ordinary
+General Public License is the better strategy to use in any particular case,
+based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not price.
+Our General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you
+wish); that you receive source code or can get it if you want it; that you can
+change the software and use pieces of it in new free programs; and that you are
+informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid distributors
+to deny you these rights or to ask you to surrender these rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for a
+fee, you must give the recipients all the rights that we gave you. You must
+make sure that they, too, receive or can get the source code. If you link other
+code with the library, you must provide complete object files to the
+recipients, so that they can relink them with the library after making changes
+to the library and recompiling it. And you must show them these terms so they
+know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the library,
+and (2) we offer you this license, which gives you legal permission to copy,
+distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no
+warranty for the free library. Also, if the library is modified by someone else
+and passed on, the recipients should know that what they have is not the
+original version, so that the original author's reputation will not be affected
+by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any free
+program. We wish to make sure that a company cannot effectively restrict the
+users of a free program by obtaining a restrictive license from a patent
+holder. Therefore, we insist that any patent license obtained for a version of
+the library must be consistent with the full freedom of use specified in this
+license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU
+General Public License. This license, the GNU Lesser General Public License,
+applies to certain designated libraries, and is quite different from the
+ordinary General Public License. We use this license for certain libraries in
+order to permit linking those libraries into non-free programs.
+
+When a program is linked with a library, whether statically or using a shared
+library, the combination of the two is legally speaking a combined work, a
+derivative of the original library. The ordinary General Public License
+therefore permits such linking only if the entire combination fits its criteria
+of freedom. The Lesser General Public License permits more lax criteria for
+linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does Less
+to protect the user's freedom than the ordinary General Public License. It also
+provides other free software developers Less of an advantage over competing
+non-free programs. These disadvantages are the reason we use the ordinary
+General Public License for many libraries. However, the Lesser license provides
+advantages in certain special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage the
+widest possible use of a certain library, so that it becomes a de-facto
+standard. To achieve this, non-free programs must be allowed to use the
+library. A more frequent case is that a free library does the same job as
+widely used non-free libraries. In this case, there is little to gain by
+limiting the free library to free software only, so we use the Lesser General
+Public License.
+
+In other cases, permission to use a particular library in non-free programs
+enables a greater number of people to use a large body of free software. For
+example, permission to use the GNU C Library in non-free programs enables many
+more people to use the whole GNU operating system, as well as its variant, the
+GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users'
+freedom, it does ensure that the user of a program that is linked with the
+Library has the freedom and the wherewithal to run that program using a
+modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification
+follow. Pay close attention to the difference between a "work based on the
+library" and a "work that uses the library". The former contains code derived
+from the library, whereas the latter must be combined with the library in order
+to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other program
+ which contains a notice placed by the copyright holder or other authorized
+ party saying it may be distributed under the terms of this Lesser General
+ Public License (also called "this License"). Each licensee is addressed as
+ "you".
+
+ A "library" means a collection of software functions and/or data prepared so
+ as to be conveniently linked with application programs (which use some of
+ those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work which has
+ been distributed under these terms. A "work based on the Library" means
+ either the Library or any derivative work under copyright law: that is to
+ say, a work containing the Library or a portion of it, either verbatim or
+ with modifications and/or translated straightforwardly into another
+ language. (Hereinafter, translation is included without limitation in the
+ term "modification".)
+
+ "Source code" for a work means the preferred form of the work for making
+ modifications to it. For a library, complete source code means all the
+ source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the library.
+
+ Activities other than copying, distribution and modification are not covered
+ by this License; they are outside its scope. The act of running a program
+ using the Library is not restricted, and output from such a program is
+ covered only if its contents constitute a work based on the Library
+ (independent of the use of the Library in a tool for writing it). Whether
+ that is true depends on what the Library does and what the program that uses
+ the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's complete
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an appropriate
+ copyright notice and disclaimer of warranty; keep intact all the notices
+ that refer to this License and to the absence of any warranty; and
+ distribute a copy of this License along with the Library.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Library or any portion of it,
+ thus forming a work based on the Library, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that you
+ also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no charge to
+ all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a table
+ of data to be supplied by an application program that uses the facility,
+ other than as an argument passed when the facility is invoked, then you
+ must make a good faith effort to ensure that, in the event an application
+ does not supply such function or table, the facility still operates, and
+ performs whatever part of its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has a purpose
+ that is entirely well-defined independent of the application. Therefore,
+ Subsection 2d requires that any application-supplied function or table used
+ by this function must be optional: if the application does not supply it,
+ the square root function must still compute square roots.)
+
+ These requirements apply to the modified work as a whole. If identifiable
+ sections of that work are not derived from the Library, and can be
+ reasonably considered independent and separate works in themselves, then
+ this License, and its terms, do not apply to those sections when you
+ distribute them as separate works. But when you distribute the same sections
+ as part of a whole which is a work based on the Library, the distribution of
+ the whole must be on the terms of this License, whose permissions for other
+ licensees extend to the entire whole, and thus to each and every part
+ regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest your
+ rights to work written entirely by you; rather, the intent is to exercise
+ the right to control the distribution of derivative or collective works
+ based on the Library.
+
+ In addition, mere aggregation of another work not based on the Library with
+ the Library (or with a work based on the Library) on a volume of a storage
+ or distribution medium does not bring the other work under the scope of this
+ License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public License
+ instead of this License to a given copy of the Library. To do this, you must
+ alter all the notices that refer to this License, so that they refer to the
+ ordinary GNU General Public License, version 2, instead of to this License.
+ (If a newer version than version 2 of the ordinary GNU General Public
+ License has appeared, then you can specify that version instead if you
+ wish.) Do not make any other change in these notices.
+
+ Once this change is made in a given copy, it is irreversible for that copy,
+ so the ordinary GNU General Public License applies to all subsequent copies
+ and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of the Library
+ into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or derivative of
+ it, under Section 2) in object code or executable form under the terms of
+ Sections 1 and 2 above provided that you accompany it with the complete
+ corresponding machine-readable source code, which must be distributed under
+ the terms of Sections 1 and 2 above on a medium customarily used for
+ software interchange.
+
+ If distribution of object code is made by offering access to copy from a
+ designated place, then offering equivalent access to copy the source code
+ from the same place satisfies the requirement to distribute the source code,
+ even though third parties are not compelled to copy the source along with
+ the object code.
+
+ 5. A program that contains no derivative of any portion of the Library, but
+ is designed to work with the Library by being compiled or linked with it, is
+ called a "work that uses the Library". Such a work, in isolation, is not a
+ derivative work of the Library, and therefore falls outside the scope of
+ this License.
+
+ However, linking a "work that uses the Library" with the Library creates an
+ executable that is a derivative of the Library (because it contains portions
+ of the Library), rather than a "work that uses the library". The executable
+ is therefore covered by this License. Section 6 states terms for
+ distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file that is
+ part of the Library, the object code for the work may be a derivative work
+ of the Library even though the source code is not. Whether this is true is
+ especially significant if the work can be linked without the Library, or if
+ the work is itself a library. The threshold for this to be true is not
+ precisely defined by law.
+
+ If such an object file uses only numerical parameters, data structure
+ layouts and accessors, and small macros and small inline functions (ten
+ lines or less in length), then the use of the object file is unrestricted,
+ regardless of whether it is legally a derivative work. (Executables
+ containing this object code plus portions of the Library will still fall
+ under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may distribute
+ the object code for the work under the terms of Section 6. Any executables
+ containing that work also fall under Section 6, whether or not they are
+ linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or link a
+ "work that uses the Library" with the Library to produce a work containing
+ portions of the Library, and distribute that work under terms of your
+ choice, provided that the terms permit modification of the work for the
+ customer's own use and reverse engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the Library
+ is used in it and that the Library and its use are covered by this License.
+ You must supply a copy of this License. If the work during execution
+ displays copyright notices, you must include the copyright notice for the
+ Library among them, as well as a reference directing the user to the copy of
+ this License. Also, you must do one of these things:
+
+ a) Accompany the work with the complete corresponding machine-readable
+ source code for the Library including whatever changes were used in the
+ work (which must be distributed under Sections 1 and 2 above); and, if
+ the work is an executable linked with the Library, with the complete
+ machine-readable "work that uses the Library", as object code and/or
+ source code, so that the user can modify the Library and then relink to
+ produce a modified executable containing the modified Library. (It is
+ understood that the user who changes the contents of definitions files in
+ the Library will not necessarily be able to recompile the application to
+ use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the Library.
+ A suitable mechanism is one that (1) uses at run time a copy of the
+ library already present on the user's computer system, rather than
+ copying library functions into the executable, and (2) will operate
+ properly with a modified version of the library, if the user installs
+ one, as long as the modified version is interface-compatible with the
+ version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least three
+ years, to give the same user the materials specified in Subsection 6a,
+ above, for a charge no more than the cost of performing this
+ distribution.
+
+ d) If distribution of the work is made by offering access to copy from a
+ designated place, offer equivalent access to copy the above specified
+ materials from the same place.
+
+ e) Verify that the user has already received a copy of these materials or
+ that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the Library"
+ must include any data and utility programs needed for reproducing the
+ executable from it. However, as a special exception, the materials to be
+ distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler, kernel,
+ and so on) of the operating system on which the executable runs, unless that
+ component itself accompanies the executable.
+
+ It may happen that this requirement contradicts the license restrictions of
+ other proprietary libraries that do not normally accompany the operating
+ system. Such a contradiction means you cannot use both them and the Library
+ together in an executable that you distribute.
+
+ 7. You may place library facilities that are a work based on the Library
+ side-by-side in a single library together with other library facilities not
+ covered by this License, and distribute such a combined library, provided
+ that the separate distribution of the work based on the Library and of the
+ other library facilities is otherwise permitted, and provided that you do
+ these two things:
+
+ a) Accompany the combined library with a copy of the same work based on
+ the Library, uncombined with any other library facilities. This must be
+ distributed under the terms of the Sections above.
+
+ b) Give prominent notice with the combined library of the fact that part
+ of it is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute the
+ Library except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense, link with, or distribute the Library
+ is void, and will automatically terminate your rights under this License.
+ However, parties who have received copies, or rights, from you under this
+ License will not have their licenses terminated so long as such parties
+ remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute the
+ Library or its derivative works. These actions are prohibited by law if you
+ do not accept this License. Therefore, by modifying or distributing the
+ Library (or any work based on the Library), you indicate your acceptance of
+ this License to do so, and all its terms and conditions for copying,
+ distributing or modifying the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+ Library), the recipient automatically receives a license from the original
+ licensor to copy, distribute, link with or modify the Library subject to
+ these terms and conditions. You may not impose any further restrictions on
+ the recipients' exercise of the rights granted herein. You are not
+ responsible for enforcing compliance by third parties with this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute so
+ as to satisfy simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not distribute
+ the Library at all. For example, if a patent license would not permit
+ royalty-free redistribution of the Library by all those who receive copies
+ directly or indirectly through you, then the only way you could satisfy both
+ it and this License would be to refrain entirely from distribution of the
+ Library.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply,
+ and the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any patents
+ or other property right claims or to contest validity of any such claims;
+ this section has the sole purpose of protecting the integrity of the free
+ software distribution system which is implemented by public license
+ practices. Many people have made generous contributions to the wide range of
+ software distributed through that system in reliance on consistent
+ application of that system; it is up to the author/donor to decide if he or
+ she is willing to distribute software through any other system and a
+ licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be a
+ consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Library under this License may add an
+ explicit geographical distribution limitation excluding those countries, so
+ that distribution is permitted only in or among countries not thus excluded.
+ In such case, this License incorporates the limitation as if written in the
+ body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new versions of
+ the Lesser General Public License from time to time. Such new versions will
+ be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Library
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Library does not specify a license version
+ number, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free programs
+ whose distribution conditions are incompatible with these, write to the
+ author to ask for permission. For software which is copyrighted by the Free
+ Software Foundation, write to the Free Software Foundation; we sometimes
+ make exceptions for this. Our decision will be guided by the two goals of
+ preserving the free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+ THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
+ THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY
+ PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+ CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO
+ LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
+ THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER
+ SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest possible
+use to the public, we recommend making it free software that everyone can
+redistribute and change. You can do so by permitting redistribution under these
+terms (or, alternatively, under the terms of the ordinary General Public
+License).
+
+To apply these terms, attach the following notices to the library. It is safest
+to attach them to the start of each source file to most effectively convey the
+exclusion of warranty; and each file should have at least the "copyright" line
+and a pointer to where the full notice is found.
+
+<one line to give the library's name and an idea of what it does.>
+
+Copyright (C) <year> <name of author>
+
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation; either version 2.1 of the License, or (at your option) any
+later version.
+
+This library 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this library; if not, write to the Free Software Foundation, Inc., 51
+Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the library, if necessary. Here is
+a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+
+the library `Frob' (a library for tweaking knobs) written
+
+by James Random Hacker.
+
+< signature of Ty Coon > , 1 April 1990
+
+Ty Coon, President of Vice
+
+That's all there is to it!
+
+------
+
+** Libsndfile; version 1.0.28 -- http://www.mega-nerd.com/libsndfile/
+Copyright (C) 2011-2016 Erik de Castro Lopo <erikd@mega-nerd.com>
+
+GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc. <http s ://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates the terms
+and conditions of version 3 of the GNU General Public License, supplemented by
+the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
+ General Public License.
+
+ "The Library" refers to a covered work governed by this License, other
+ than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided by
+ the Library, but which is not otherwise based on the Library. Defining a
+ subclass of a class defined by the Library is deemed a mode of using an
+ interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+ Application with the Library. The particular version of the Library with
+ which the Combined Work was made is also called the "Linked Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+ Corresponding Source for the Combined Work, excluding any source code for
+ portions of the Combined Work that, considered in isolation, are based on
+ the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the object
+ code and/or source code for the Application, including any data and
+ utility programs needed for reproducing the Combined Work from the
+ Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License without
+ being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a facility
+ refers to a function or data to be supplied by an Application that uses the
+ facility (other than as an argument passed when the facility is invoked),
+ then you may convey a copy of the modified version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the function or
+ data, the facility still operates, and performs whatever part of its
+ purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of this
+ License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from a
+ header file that is part of the Library. You may convey such object code
+ under terms of your choice, provided that, if the incorporated material is
+ not limited to numerical parameters, data structure layouts and accessors,
+ or small macros, inline functions and templates (ten or fewer lines in
+ length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are covered by
+ this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that, taken
+ together, effectively do not restrict modification of the portions of the
+ Library contained in the Combined Work and reverse engineering for debugging
+ such modifications, if you also do each of the following:
+
+ a) Give prominent notice with each copy of the Combined Work that the
+ Library is used in it and that the Library and its use are covered by
+ this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+
+ c) For a Combined Work that displays copyright notices during execution,
+ include the copyright notice for the Library among these notices, as well
+ as a reference directing the user to the copies of the GNU GPL and this
+ license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form suitable
+ for, and under terms that permit, the user to recombine or relink the
+ Application with a modified version of the Linked Version to produce a
+ modified Combined Work, in the manner specified by section 6 of the
+ GNU GPL for conveying Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time a copy
+ of the Library already present on the user's computer system, and (b)
+ will operate properly with a modified version of the Library that is
+ interface-compatible with the Linked Version.
+
+ e) Provide Installation Information, but only if you would otherwise be
+ required to provide such information under section 6 of the GNU GPL, and
+ only to the extent that such information is necessary to install and
+ execute a modified version of the Combined Work produced by recombining
+ or relinking the Application with a modified version of the Linked
+ Version. (If you use option 4d0, the Installation Information must
+ accompany the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL for
+ conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the Library side
+ by side in a single library together with other library facilities that are
+ not Applications and are not covered by this License, and convey such a
+ combined library under terms of your choice, if you do both of the
+ following:
+
+ a) Accompany the combined library with a copy of the same work based on
+ the Library, uncombined with any other library facilities, conveyed under
+ the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it is a
+ work based on the Library, and explaining where to find the accompanying
+ uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions of the
+ GNU Lesser General Public License from time to time. Such new versions will
+ be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Library as you
+ received it specifies that a certain numbered version of the GNU Lesser
+ General Public License "or any later version" applies to it, you have the
+ option of following the terms and conditions either of that published
+ version or of any later version published by the Free Software Foundation.
+ If the Library as you received it does not specify a version number of the
+ GNU Lesser General Public License, you may choose any version of the GNU
+ Lesser General Public License ever published by the Free Software
+ Foundation.
+
+ If the Library as you received it specifies that a proxy can decide whether
+ future versions of the GNU Lesser General Public License shall apply, that
+ proxy's public statement of acceptance of any version is permanent
+ authorization for you to choose that version for the Library.
+
+------
+
+** LIBPNG; version 1.6.37 -- http://prdownloads.sourceforge.net/libpng
+* Copyright (c) 1995-2019 The PNG Reference Library Authors.
+ * Copyright (c) 2018-2019 Cosmin Truta.
+ * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
+ * Copyright (c) 1996-1997 Andreas Dilger.
+ * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+
+This copy of the libpng notices is provided for your convenience. In case of
+any discrepancy between this copy and the notices in the file png.h that is
+included in the libpng distribution, the latter shall prevail.
+
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+If you modify libpng you may insert additional notices immediately following
+this sentence.
+
+This code is released under the libpng license.
+
+libpng versions 1.2.6, August 15, 2004, through 1.4.5, December 9, 2010, are
+Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are distributed
+according to the same disclaimer and license as libpng-1.2.5 with the following
+individual added to the list of Contributing Authors
+
+Cosmin Truta
+
+libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are
+
+Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to
+the same disclaimer and license as libpng-1.0.6 with the following individuals
+added to the list of Contributing Authors
+
+Simon-Pierre Cadieux
+
+Eric S. Raymond
+
+Gilles Vollant
+
+and with the following additions to the disclaimer:
+
+There is no warranty against interference with your enjoyment of the library or
+against infringement. There is no warranty that our efforts or the library will
+fulfill any of your particular purposes or needs. This library is provided with
+all faults, and the entire risk of satisfactory quality, performance, accuracy,
+and effort is with the user.
+
+libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+
+Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according
+to the same disclaimer and license as libpng-0.96, with the following
+individuals added to the list of Contributing Authors:
+
+Tom Lane
+
+Glenn Randers-Pehrson
+
+Willem van Schaik
+
+libpng versions 0.89, June 1996, through 0.96, May 1997, are
+
+Copyright (c) 1996, 1997 Andreas Digger
+
+Distributed according to the same disclaimer and license as libpng-0.88, with
+the following individuals added to the list of Contributing Authors:
+
+John Bowler
+
+Kevin Bracey
+
+Sam Bushell
+
+Magnus Holmgren
+
+Greg Roelofs
+
+Tom Tanner
+
+libpng versions 0.5, May 1995, through 0.88, January 1996, are
+
+Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+
+For the purposes of this copyright and license, "Contributing Authors" is
+defined as the following set of individuals:
+
+Andreas Dilger
+
+Dave Martindale
+
+Guy Eric Schalnat
+
+Paul Schmidt
+
+Tim Wegner
+
+The PNG Reference Library is supplied "AS IS". The Contributing Authors and
+Group 42, Inc. disclaim all warranties, expressed or implied, including,
+without limitation, the warranties of merchantability and of fitness for any
+purpose. The Contributing Authors and Group 42, Inc. assume no liability for
+direct, indirect, incidental, special, exemplary, or consequential damages,
+which may result from the use of the PNG Reference Library, even if advised of
+the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this source
+code, or portions hereof, for any purpose, without fee, subject to the
+following restrictions:
+
+ 1. The origin of this source code must not be misrepresented.
+
+ 2. Altered versions must be plainly marked as such and must not be
+ misrepresented as being the original source.
+
+ 3. This Copyright notice may not be removed or altered from any source or
+ altered source distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit, without fee,
+and encourage the use of this source code as a component to supporting the PNG
+file format in commercial products. If you use this source code in a product,
+acknowledgment is not required but would be appreciated.
+
+A "png_get_copyright" function is available, for convenient use in "about"
+boxes and the like:
+
+printf("%s",png_get_copyright(NULL));
+
+Also, the PNG logo (in PNG format, of course) is supplied in the files
+"pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+
+Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a
+certification mark of the Open Source Initiative.
+
+Glenn Randers-Pehrson
+
+glennrp at users.sourceforge.net
+
+December 9, 2010
+
+------
+
+** Libxml2; version 2.9.10 -- http://xmlsoft.org/
+Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.
+** Mesa 3D; version 18.3.1 -- https://www.mesa3d.org/
+Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
+** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA
+Copyright (c) 2008-2009 NetAllied Systems GmbH
+** PugiXML; version 1.10 -- http://pugixml.org/
+Copyright (c) 2006-2020 Arseny Kapoulkine
+** QuadriFlow; version 27a6867 -- https://github.com/hjwdzh/QuadriFlow
+Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner,
+Jonathan Shewchuk and Leonidas Guibas. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+------
+
+** NanoVDB; version e62f7a0bf1e27397223c61ddeaaf57edf111b77f --
+https://github.com/AcademySoftwareFoundation/openvdb
+Copyright Contributors to the OpenVDB Project
+** OpenVDB; version 7.0.0 -- http://www.openvdb.org/
+Copyright Contributors to the OpenVDB Project
+
+Mozilla Public License Version 2.0
+
+ 1. Definitions
+
+ 1.1. "Contributor" means each individual or legal entity that creates,
+ contributes to the creation of, or owns Covered Software.
+
+ 1.2. "Contributor Version" means the combination of the Contributions of
+ others (if any) used by a Contributor and that particular Contributor's
+ Contribution.
+
+ 1.3. "Contribution" means Covered Software of a particular Contributor.
+
+ 1.4. "Covered Software" means Source Code Form to which the initial
+ Contributor has attached the notice in Exhibit A, the Executable Form of
+ such Source Code Form, and Modifications of such Source Code Form, in
+ each case including portions thereof.
+
+ 1.5. "Incompatible With Secondary Licenses" means
+
+ (a) that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the terms of
+ a Secondary License.
+
+ 1.6. "Executable Form" means any form of the work other than Source Code
+ Form.
+
+ 1.7. "Larger Work" means a work that combines Covered Software with other
+ material, in a separate file or files, that is not Covered Software.
+
+ 1.8. "License" means this document.
+
+ 1.9. "Licensable" means having the right to grant, to the maximum extent
+ possible, whether at the time of the initial grant or subsequently, any
+ and all of the rights conveyed by this License.
+
+ 1.10. "Modifications" means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+ 1.11. "Patent Claims" of a Contributor means any patent claim(s),
+ including without limitation, method, process, and apparatus claims, in
+ any patent Licensable by such Contributor that would be infringed, but
+ for the grant of the License, by the making, using, selling, offering for
+ sale, having made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+ 1.12. "Secondary License" means either the GNU General Public License,
+ Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU
+ Affero General Public License, Version 3.0, or any later versions of
+ those licenses.
+
+ 1.13. "Source Code Form" means the form of the work preferred for making
+ modifications.
+
+ 1.14. "You" (or "Your") means an individual or a legal entity exercising
+ rights under this License. For legal entities, "You" includes any entity
+ that controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct or
+ indirect, to cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty percent (50%)
+ of the outstanding shares or beneficial ownership of such entity.
+
+ 2. License Grants and Conditions
+
+ 2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by such Contributor to use, reproduce, make
+ available, modify, display, perform, distribute, and otherwise exploit
+ its Contributions, either on an unmodified basis, with Modifications,
+ or as part of a Larger Work; and
+
+ (b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+ 2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution
+ become effective for each Contribution on the date the Contributor first
+ distributes such Contribution.
+
+ 2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under
+ this License. No additional rights or licenses will be implied from the
+ distribution or licensing of Covered Software under this License.
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
+ Contributor:
+
+ (a) for any code that a Contributor has removed from Covered Software;
+ or
+
+ (b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ (c) under Patent Claims infringed by Covered Software in the absence
+ of its Contributions.
+
+ This License does not grant any rights in the trademarks, service marks,
+ or logos of any Contributor (except as may be necessary to comply with
+ the notice requirements in Section 3.4).
+
+ 2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this
+ License (see Section 10.2) or under the terms of a Secondary License (if
+ permitted under the terms of Section 3.3).
+
+ 2.5. Representation
+
+ Each Contributor represents that the Contributor believes its
+ Contributions are its original creation(s) or it has sufficient rights to
+ grant the rights to its Contributions conveyed by this License.
+
+ 2.6. Fair Use
+
+ This License is not intended to limit any rights You have under
+ applicable copyright doctrines of fair use, fair dealing, or other
+ equivalents.
+
+ 2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+ 3. Responsibilities
+
+ 3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under
+ the terms of this License. You must inform recipients that the Source
+ Code Form of the Covered Software is governed by the terms of this
+ License, and how they can obtain a copy of this License. You may not
+ attempt to alter or restrict the recipients' rights in the Source Code
+ Form.
+
+ 3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ (a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more than
+ the cost of distribution to the recipient; and
+
+ (b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter the
+ recipients' rights in the Source Code Form under this License.
+
+ 3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for
+ the Covered Software. If the Larger Work is a combination of Covered
+ Software with a work governed by one or more Secondary Licenses, and the
+ Covered Software is not Incompatible With Secondary Licenses, this
+ License permits You to additionally distribute such Covered Software
+ under the terms of such Secondary License(s), so that the recipient of
+ the Larger Work may, at their option, further distribute the Covered
+ Software under the terms of either this License or such Secondary
+ License(s).
+
+ 3.4. Notices
+
+ You may not remove or alter the substance of any license notices
+ (including copyright notices, patent notices, disclaimers of warranty, or
+ limitations of liability) contained within the Source Code Form of the
+ Covered Software, except that You may alter any license notices to the
+ extent required to remedy known factual inaccuracies.
+
+ 3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on
+ behalf of any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity, or liability obligation is offered by
+ You alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+ 4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute, judicial
+ order, or regulation then You must: (a) comply with the terms of this
+ License to the maximum extent possible; and (b) describe the limitations and
+ the code they affect. Such description must be placed in a text file
+ included with all distributions of the Covered Software under this License.
+ Except to the extent prohibited by statute or regulation, such description
+ must be sufficiently detailed for a recipient of ordinary skill to be able
+ to understand it.
+
+ 5. Termination
+
+ 5.1. The rights granted under this License will terminate automatically
+ if You fail to comply with any of its terms. However, if You become
+ compliant, then the rights granted under this License from a particular
+ Contributor are reinstated (a) provisionally, unless and until such
+ Contributor explicitly and finally terminates Your grants, and (b) on an
+ ongoing basis, if such Contributor fails to notify You of the
+ non-compliance by some reasonable means prior to 60 days after You have
+ come back into compliance. Moreover, Your grants from a particular
+ Contributor are reinstated on an ongoing basis if such Contributor
+ notifies You of the non-compliance by some reasonable means, this is the
+ first time You have received notice of non-compliance with this License
+ from such Contributor, and You become compliant prior to 30 days after
+ Your receipt of the notice.
+
+ 5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions,
+ counter-claims, and cross-claims) alleging that a Contributor Version
+ directly or indirectly infringes any patent, then the rights granted to
+ You by any and all Contributors for the Covered Software under Section
+ 2.1 of this License shall terminate.
+
+ 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end
+ user license agreements (excluding distributors and resellers) which have
+ been validly granted by You or Your distributors under this License prior
+ to termination shall survive termination.
+
+ 6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an "as is" basis, without
+ warranty of any kind, either expressed, implied, or statutory, including,
+ without limitation, warranties that the Covered Software is free of defects,
+ merchantable, fit for a particular purpose or non-infringing. The entire
+ risk as to the quality and performance of the Covered Software is with You.
+ Should any Covered Software prove defective in any respect, You (not any
+ Contributor) assume the cost of any necessary servicing, repair, or
+ correction. This disclaimer of warranty constitutes an essential part of
+ this License. No use of any Covered Software is authorized under this
+ License except under this disclaimer.
+
+ 7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from
+ such party's negligence to the extent applicable law prohibits such
+ limitation. Some jurisdictions do not allow the exclusion or limitation of
+ incidental or consequential damages, so this exclusion and limitation may
+ not apply to You.
+
+ 8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts of
+ a jurisdiction where the defendant maintains its principal place of business
+ and such litigation shall be governed by laws of that jurisdiction, without
+ reference to its conflict-of-law provisions. Nothing in this Section shall
+ prevent a party's ability to bring cross-claims or counter-claims.
+
+ 9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject matter
+ hereof. If any provision of this License is held to be unenforceable, such
+ provision shall be reformed only to the extent necessary to make it
+ enforceable. Any law or regulation which provides that the language of a
+ contract shall be construed against the drafter shall not be used to
+ construe this License against a Contributor.
+
+ 10. Versions of the License
+
+ 10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+ 10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version of
+ the License under which You originally received the Covered Software, or
+ under the terms of any subsequent version published by the license
+ steward.
+
+ 10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a modified
+ version of this License if you rename the license and remove any
+ references to the name of the license steward (except to note that such
+ modified license differs from this License).
+
+ 10.4. Distributing Source Code Form that is Incompatible With Secondary
+ Licenses
+
+ If You choose to distribute Source Code Form that is Incompatible With
+ Secondary Licenses under the terms of this version of the License, the
+ notice described in Exhibit B of this License must be attached. Exhibit A
+ - Source Code Form License Notice
+
+This Source Code Form is subject to the terms of the Mozilla Public License, v.
+2.0. If a copy of the MPL was not distributed with this file, You can obtain
+one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file, then
+You may include the notice in a location (such as a LICENSE file in a relevant
+directory) where a recipient would be likely to look for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+This Source Code Form is "Incompatible With Secondary Licenses", as defined by
+the Mozilla Public License, v. 2.0.
+
+------
+
+** Bullet Continuous Collision Detection and Physics Library; version 3.07 --
+http://continuousphysics.com/Bullet/
+Bullet Continuous Collision Detection and Physics Library
+Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
+** SDL; version 2.0.12 -- https://www.libsdl.org
+Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
+** zlib; version 1.2.11 -- https://zlib.net
+Copyright (C) 1995-2017 Jean-loup Gailly
+
+zlib License Copyright (c) <year> <copyright holders>
+
+This software is provided 'as-is', without any express or implied warranty. In
+no event will the authors be held liable for any damages arising from the use
+of this software.
+
+Permission is granted to anyone to use this software for any purpose, including
+commercial applications, and to alter it and redistribute it freely, subject to
+the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software in a
+ product, an acknowledgment in the product documentation would be appreciated
+ but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+------
+
+** LibTIFF; version 4.1.0 -- http://www.libtiff.org/
+Copyright (c) 1988-1997 Sam Leffler
+Copyright (c) 1991-1997 Silicon Graphics, Inc.
+
+Copyright (c) 1988-1997 Sam Leffler
+
+Copyright (c) 1991-1997 Silicon Graphics, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that (i)
+the above copyright notices and this permission notice appear in all copies of
+the software and related documentation, and (ii) the names of Sam Leffler and
+Silicon Graphics may not be used in any advertising or publicity relating to
+the software without the specific, prior written permission of Sam Leffler and
+Silicon Graphics.
+
+THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
+IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL,
+INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED
+OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+------
+
+** The LLVM Compiler Infrastructure; version 9.0.1 --
+https://github.com/llvm/llvm-project/
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
+------
+
+** OpenSubdiv; version 3.4.3 -- http://graphics.pixar.com/opensubdiv
+Copyright 2013 Pixar
+** Universal Scene Description; version 20.05 -- http://www.openusd.org/
+Copyright 2016 Pixar
+
+Licensed under the Apache License, Version 2.0 (the "Apache License") with the
+following modification; you may not use this file except in compliance with the
+Apache License and the following modification to it:
+
+Section 6. Trademarks. is deleted and replaced with:
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor and its affiliates,
+except as required to comply with Section 4(c) of the License and to reproduce
+the content of the NOTICE file.
+
+------
+
+** libjpeg-turbo; version 2.0.4 --
+https://github.com/libjpeg-turbo/libjpeg-turbo/
+Copyright (C)2009-2020 D. R. Commander. All Rights Reserved.
+Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+Neither the name of the libjpeg-turbo Project nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------
+
+** Boost C++ Libraries; version 1.70.0 -- https://www.boost.org/
+-
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+------
+
+** Alembic; version 1.7.12 -- https://github.com/alembic/alembic
+TM & © 2009-2015 Lucasfilm Entertainment Company Ltd. or Lucasfilm Ltd.
+All rights reserved.
+
+Industrial Light & Magic, ILM and the Bulb and Gear design logo are all
+registered trademarks or service marks of Lucasfilm Ltd.
+
+© 2009-2015 Sony Pictures Imageworks Inc. All rights reserved.
+
+TM & © 2009-2015 Lucasfilm Entertainment Company Ltd. or Lucasfilm Ltd.
+All rights reserved.
+
+Industrial Light & Magic, ILM and the Bulb and Gear design logo are all
+registered trademarks or service marks of Lucasfilm Ltd.
+
+© 2009-2015 Sony Pictures Imageworks Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+* Neither the name of Industrial Light & Magic nor the names of
+its contributors may be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-------------------------------------------------------------------------------
+
+ALEMBIC ATTACHMENT A —
+REQUIRED NOTICES FOR DISTRIBUTION
+
+The Alembic Software is distributed along with certain third party
+components licensed under various open source software licenses ("Open
+Source Components"). In addition to the warranty disclaimers contained
+in the open source licenses found below, Industrial Light & Magic, a
+division of Lucasfilm Entertainment Company Ltd. ("ILM") makes the
+following disclaimers regarding the Open Source Components on behalf of
+itself, the copyright holders, contributors, and licensors of such Open
+Source Components:
+
+TO THE FULLEST EXTENT PERMITTED UNDER APPLICABLE LAW, THE OPEN SOURCE
+COMPONENTS ARE PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS,
+LICENSORS, AND ILM "AS IS" AND ANY REPRESENTATIONS OR WARRANTIES OF ANY
+KIND, WHETHER ORAL OR WRITTEN, WHETHER EXPRESS, IMPLIED, OR ARISING BY
+STATUTE, CUSTOM, COURSE OF DEALING, OR TRADE USAGE, INCLUDING WITHOUT
+LIMITATION THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR
+A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT
+WILL THE COPYRIGHT OWNER, CONTRIBUTORS, LICENSORS, OR ILM AND/OR ITS
+AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE OPEN
+SOURCE COMPONENTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Boost C++ Libraries
+------------------------------------------------------------------------
+
+Boost Software License – Version 1.0 August 17th, 2003 Permission is
+hereby granted, free of charge, to any person or organization obtaining
+a copy of the software and accompanying documentation covered by this
+license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of
+the Software, and to permit third-parties to whom the Software is
+furnished to do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement,
+including the above license grant, this restriction and the following
+disclaimer, must be included in all copies of the Software, in whole or
+in part, and all derivative works of the Software, unless such copies or
+derivative works are solely in the form of machine-executable object
+code generated by a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
+DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,
+WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+------
+
+** WC Width; version 2007-05-26 -- http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+
+Permission to use, copy, modify, and distribute this software
+for any purpose and without fee is hereby granted. The author
+disclaims all warranties with regard to this software.
+
+------
+
+** Python; version 3.7.7 -- https://www.python.org
+Copyright (c) 2001-2020 Python Software Foundation. All rights reserved.
+
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC. Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team. In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation. In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition). Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+ Release Derived Year Owner GPL-
+ from compatible? (1)
+
+ 0.9.0 thru 1.2 1991-1995 CWI yes
+ 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
+ 1.6 1.5.2 2000 CNRI no
+ 2.0 1.6 2000 BeOpen.com no
+ 1.6.1 1.6 2001 CNRI yes (2)
+ 2.1 2.0+1.6.1 2001 PSF no
+ 2.0.1 2.0+1.6.1 2001 PSF yes
+ 2.1.1 2.1+2.0.1 2001 PSF yes
+ 2.1.2 2.1.1 2002 PSF yes
+ 2.1.3 2.1.2 2002 PSF yes
+ 2.2 and above 2.1.1 2001-now PSF yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+ the GPL. All Python licenses, unlike the GPL, let you distribute
+ a modified version without making your changes open source. The
+ GPL-compatible licenses make it possible to combine Python with
+ other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+ because its license has a choice of law clause. According to
+ CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+ is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+Python software and documentation are licensed under the
+Python Software Foundation License Version 2.
+
+Starting with Python 3.8.6, examples, recipes, and other code in
+the documentation are dual licensed under the PSF License Version 2
+and the Zero-Clause BSD license.
+
+Some software incorporated into Python is under different licenses.
+The licenses are listed with code falling under that license.
+
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software
+Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions. Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee. This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party. As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee. Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement. This Agreement together with
+Python 1.6.1 may be located on the Internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013. This
+Agreement may also be obtained from a proxy server on the Internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee. This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+ ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands. All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
+----------------------------------------------------------------------
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+------
+
+** Jemalloc; version 5.2.1 -- https://github.com/jemalloc/jemalloc
+Copyright (C) 2002-present Jason Evans <jasone@canonware.com>.
+All rights reserved.
+Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
+Copyright (C) 2009-present Facebook, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice(s),
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice(s),
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------
+
+** The OpenGL Extension Wrangler Library; version 2.0.0 --
+http://glew.sourceforge.net/
+Copyright (C) 2008-2015, Nigel Stewart <nigels[]users sourceforge net>
+Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org>
+Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org>
+Copyright (C) 2002, Lev Povalahev
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+* The name of the author may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Mesa 3-D graphics library
+Version: 7.0
+
+Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Copyright (c) 2007 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. \ No newline at end of file
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 1191a172ac389e3d068a8ef7d16f36457e67e3b
+Subproject a3fa40ec0ba525bc96cbfad49f854a0230b0524
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject eae381b698248e70e4a3c62bdf239f9d5a0470a
+Subproject d71985e901986970dfc86c3d5d1124d0f8c2751
diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py
index 40b76b617b3..fd4488ecd73 100644
--- a/release/scripts/modules/bl_i18n_utils/utils.py
+++ b/release/scripts/modules/bl_i18n_utils/utils.py
@@ -185,9 +185,9 @@ def list_po_dir(root_path, settings):
"""
Generator. List given directory (expecting one sub-directory per languages)
and return all files matching languages listed in settings.
-
+
Yield tuples (can_use, uid, num_id, name, isocode, po_path)
-
+
Note that po_path may not actually exists.
"""
isocodes = ((e, os.path.join(root_path, e, e + ".po")) for e in os.listdir(root_path))
diff --git a/release/scripts/modules/bl_i18n_utils/utils_cli.py b/release/scripts/modules/bl_i18n_utils/utils_cli.py
index d38911c122d..76dd8a740c5 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_cli.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_cli.py
@@ -103,22 +103,22 @@ def main():
sub_parsers = parser.add_subparsers()
sub_parser = sub_parsers.add_parser('update_po', help="Update a PO file from a given POT template file")
- sub_parser.add_argument('--template', metavar='template.pot', required=True,
+ sub_parser.add_argument('--template', metavar='template.pot', required=True,
help="The source pot file to use as template for the update.")
sub_parser.add_argument('--dst', metavar='dst.po', required=True, help="The destination po to update.")
- sub_parser.set_defaults(func=update_po)
+ sub_parser.set_defaults(func=update_po)
sub_parser = sub_parsers.add_parser('cleanup_po',
help="Cleanup a PO file (check for and fix some common errors, remove commented messages).")
sub_parser.add_argument('--src', metavar='src.po', required=True, help="The source po file to clean up.")
sub_parser.add_argument('--dst', metavar='dst.po', help="The destination po to write to.")
- sub_parser.set_defaults(func=cleanup_po)
+ sub_parser.set_defaults(func=cleanup_po)
sub_parser = sub_parsers.add_parser('strip_po',
help="Reduce all non-essential data from given PO file (reduce its size).")
sub_parser.add_argument('--src', metavar='src.po', required=True, help="The source po file to strip.")
sub_parser.add_argument('--dst', metavar='dst.po', help="The destination po to write to.")
- sub_parser.set_defaults(func=strip_po)
+ sub_parser.set_defaults(func=strip_po)
sub_parser = sub_parsers.add_parser('rtl_process_po',
help="Pre-process PO files for RTL languages.")
@@ -128,7 +128,7 @@ def main():
sub_parser = sub_parsers.add_parser('language_menu',
help="Generate the text file used by Blender to create its language menu.")
- sub_parser.set_defaults(func=language_menu)
+ sub_parser.set_defaults(func=language_menu)
args = parser.parse_args(sys.argv[1:])
diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py
index 091cdbc2642..645a145f994 100644
--- a/release/scripts/modules/bl_keymap_utils/io.py
+++ b/release/scripts/modules/bl_keymap_utils/io.py
@@ -222,12 +222,21 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
fw("]\n")
fw("\n\n")
fw("if __name__ == \"__main__\":\n")
+
+ # We could remove this in the future, as loading new key-maps in older Blender versions
+ # makes less and less sense as Blender changes.
+ fw(" # Only add keywords that are supported.\n")
+ fw(" from bpy.app import version as blender_version\n")
+ fw(" keywords = {}\n")
+ fw(" if blender_version >= (2, 92, 0):\n")
+ fw(" keywords[\"keyconfig_version\"] = keyconfig_version\n")
+
fw(" import os\n")
fw(" from bl_keymap_utils.io import keyconfig_import_from_data\n")
fw(" keyconfig_import_from_data(\n")
fw(" os.path.splitext(os.path.basename(__file__))[0],\n")
fw(" keyconfig_data,\n")
- fw(" keyconfig_version=keyconfig_version,\n")
+ fw(" **keywords,\n")
fw(" )\n")
diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py
index 1caef074d43..cb990b014a1 100644
--- a/release/scripts/modules/bpy_extras/__init__.py
+++ b/release/scripts/modules/bpy_extras/__init__.py
@@ -24,6 +24,7 @@ Utility modules associated with the bpy module.
__all__ = (
"anim_utils",
+ "asset_utils",
"object_utils",
"io_utils",
"image_utils",
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
new file mode 100644
index 00000000000..db982e119d4
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -0,0 +1,63 @@
+# ##### 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>
+
+"""
+Helpers for asset management tasks.
+"""
+
+import bpy
+from bpy.types import (
+ Context,
+)
+
+__all__ = (
+ "SpaceAssetInfo",
+)
+
+class SpaceAssetInfo:
+ @classmethod
+ def is_asset_browser(cls, space_data: bpy.types.Space):
+ return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
+
+ @classmethod
+ def is_asset_browser_poll(cls, context: Context):
+ return cls.is_asset_browser(context.space_data)
+
+ @classmethod
+ def get_active_asset(cls, context: Context):
+ if hasattr(context, "active_file"):
+ active_file = context.active_file
+ return active_file.asset_data if active_file else None
+
+class AssetBrowserPanel:
+ bl_space_type = 'FILE_BROWSER'
+
+ @classmethod
+ def poll(cls, context):
+ return SpaceAssetInfo.is_asset_browser_poll(context)
+
+class AssetMetaDataPanel:
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOL_PROPS'
+
+ @classmethod
+ def poll(cls, context):
+ active_file = context.active_file
+ return SpaceAssetInfo.is_asset_browser_poll(context) and active_file and active_file.asset_data
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 8171b9ce1a4..d3990851e2c 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -755,6 +755,7 @@ def km_property_editor(_params):
# ShaderFX panels
("object.shaderfx_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.shaderfx_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
# Constraint panels
("constraint.delete", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
@@ -1216,7 +1217,7 @@ def km_view3d(params):
("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.use_snap')]}),
- op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", False)]),
+ op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", True)]),
("object.transform_axis_target", {"type": 'T', "value": 'PRESS', "shift": True}, None),
("transform.skin_resize", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
])
@@ -2500,6 +2501,8 @@ def km_sequencer(params):
("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True},
+ {"properties": [("keep_offset", True)]}),
("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None),
("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 54731c7139f..4b8a470a3ae 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -454,6 +454,17 @@ def km_property_editor(params):
("object.modifier_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}),
("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
("object.modifier_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ # Grease pencil modifier panels
+ ("object.gpencil_modifier_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ # ShaderFX panels
+ ("object.shaderfx_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("objectshaderfx_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ # Constraint panels
+ ("constraint.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}),
+ ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
])
return keymap
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 71b2de41d9e..e91d3b3ce60 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -27,6 +27,7 @@ if "bpy" in locals():
_modules = [
"add_mesh_torus",
"anim",
+ "assets",
"clip",
"console",
"constraint",
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py
index bb414b5ff89..e1d7f2057d2 100644
--- a/release/scripts/startup/bl_operators/anim.py
+++ b/release/scripts/startup/bl_operators/anim.py
@@ -307,7 +307,7 @@ class NLA_OT_bake(Operator):
class ClearUselessActions(Operator):
- """Mark actions with no F-Curves for deletion after save & reload of """ \
+ """Mark actions with no F-Curves for deletion after save and reload of """ \
"""file preserving \"action libraries\""""
bl_idname = "anim.clear_useless_actions"
bl_label = "Clear Useless Actions"
diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py
new file mode 100644
index 00000000000..317555280e5
--- /dev/null
+++ b/release/scripts/startup/bl_operators/assets.py
@@ -0,0 +1,75 @@
+# ##### 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 #####
+
+import bpy
+
+from bpy_extras.asset_utils import (
+ SpaceAssetInfo,
+)
+
+
+class ASSET_OT_tag_add(bpy.types.Operator):
+ """Add a new keyword tag to the active asset"""
+
+ bl_idname = "asset.tag_add"
+ bl_label = "Add Asset Tag"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context)
+
+ def execute(self, context):
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ active_asset.tags.new("Unnamed Tag")
+
+ return {'FINISHED'}
+
+
+class ASSET_OT_tag_remove(bpy.types.Operator):
+ """Remove an existing keyword tag from the active asset"""
+
+ bl_idname = "asset.tag_remove"
+ bl_label = "Remove Asset Tag"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ if not SpaceAssetInfo.is_asset_browser_poll(context):
+ return False
+
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ if not active_asset:
+ return False
+
+ return active_asset.active_tag in range(len(active_asset.tags))
+
+ def execute(self, context):
+ active_asset = SpaceAssetInfo.get_active_asset(context)
+ tag = active_asset.tags[active_asset.active_tag]
+
+ active_asset.tags.remove(tag)
+ active_asset.active_tag -= 1
+
+ return {'FINISHED'}
+
+
+classes = (
+ ASSET_OT_tag_add,
+ ASSET_OT_tag_remove,
+)
diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py
index 7d7e3793dba..b94104d10a9 100644
--- a/release/scripts/startup/bl_operators/geometry_nodes.py
+++ b/release/scripts/startup/bl_operators/geometry_nodes.py
@@ -18,6 +18,7 @@
import bpy
+
def geometry_node_group_empty_new(context):
group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree')
group.inputs.new('NodeSocketGeometry', "Geometry")
@@ -33,6 +34,7 @@ def geometry_node_group_empty_new(context):
return group
+
def geometry_modifier_poll(context) -> bool:
ob = context.object
@@ -42,6 +44,7 @@ def geometry_modifier_poll(context) -> bool:
return True
+
class NewGeometryNodesModifier(bpy.types.Operator):
"""Create a new modifier with a new geometry node group"""
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index 87973ac2c45..92eb47cea0f 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -223,7 +223,7 @@ class SelectHierarchy(Operator):
class SubdivisionSet(Operator):
- """Sets a Subdivision Surface Level (1-5)"""
+ """Sets a Subdivision Surface level (1 to 5)"""
bl_idname = "object.subdivision_set"
bl_label = "Subdivision Set"
@@ -888,7 +888,7 @@ class LoadImageAsEmpty:
filter_folder: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'})
view_align: BoolProperty(
- name="Align to view",
+ name="Align to View",
default=True,
)
diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py
index ec9b098be2c..cc6724a620e 100644
--- a/release/scripts/startup/bl_operators/object_align.py
+++ b/release/scripts/startup/bl_operators/object_align.py
@@ -358,7 +358,7 @@ from bpy.props import (
class AlignObjects(Operator):
- """Align Objects"""
+ """Align objects"""
bl_idname = "object.align"
bl_label = "Align Objects"
bl_options = {'REGISTER', 'UNDO'}
@@ -386,7 +386,7 @@ class AlignObjects(Operator):
name="Relative To",
description="Reference location to align to",
items=(
- ('OPT_1', "Scene Origin", "Use the Scene Origin as the position for the selected objects to align to"),
+ ('OPT_1', "Scene Origin", "Use the scene origin as the position for the selected objects to align to"),
('OPT_2', "3D Cursor", "Use the 3D cursor as the position for the selected objects to align to"),
('OPT_3', "Selection", "Use the selected objects as the position for the selected objects to align to"),
('OPT_4', "Active", "Use the active object as the position for the selected objects to align to"),
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 0f4eb8a8507..0600536cb66 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -134,7 +134,7 @@ class QuickExplode(ObjectModeOperator, Operator):
default='EXPLODE',
)
amount: IntProperty(
- name="Amount of pieces",
+ name="Number of Pieces",
min=2, max=10000,
soft_min=2, soft_max=10000,
default=100,
@@ -337,7 +337,7 @@ class QuickSmoke(ObjectModeOperator, Operator):
items=(
('SMOKE', "Smoke", ""),
('FIRE', "Fire", ""),
- ('BOTH', "Smoke + Fire", ""),
+ ('BOTH', "Smoke & Fire", ""),
),
default='SMOKE',
)
@@ -573,6 +573,7 @@ class QuickLiquid(Operator):
return {'FINISHED'}
+
classes = (
QuickExplode,
QuickFur,
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 8201ce080b1..c663a736441 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -893,6 +893,7 @@ class GreasePencilLayerDisplayPanel:
row = layout.row(align=True)
row.prop(gpl, "use_solo_mode", text="Show Only on Keyframed")
+
class GreasePencilFlipTintColors(Operator):
bl_label = "Flip Colors"
bl_idname = "gpencil.tint_flip"
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index 6aaec9940e8..47ab98386f4 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -42,6 +42,9 @@ class MATERIAL_UL_matslots(UIList):
# ob = data
slot = item
ma = slot.material
+
+ layout.context_pointer_set("id", ma)
+
if self.layout_type in {'DEFAULT', 'COMPACT'}:
if ma:
layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 1ae1826b609..5d241e8e216 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -550,7 +550,6 @@ def brush_settings(layout, context, brush, popover=False):
if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt:
layout.prop(brush, "tilt_strength_factor", slider=True)
-
row = layout.row(align=True)
row.prop(brush, "hardness", slider=True)
row.prop(brush, "invert_hardness_pressure", text="")
@@ -765,6 +764,10 @@ def brush_settings(layout, context, brush, popover=False):
col.prop(brush, "surface_smooth_current_vertex")
col.prop(brush, "surface_smooth_iterations")
+ elif sculpt_tool == 'DISPLACEMENT_SMEAR':
+ col = layout.column()
+ col.prop(brush, "smear_deform_type")
+
elif sculpt_tool == 'MASK':
layout.row().prop(brush, "mask_tool", expand=True)
@@ -1194,6 +1197,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row.prop(brush, "size", text="Radius")
row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
+ row.prop(gp_settings, "use_default_eraser", text="")
row = layout.row(align=True)
row.prop(gp_settings, "eraser_mode", expand=True)
diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py
index 27df3b10853..b5e6942f19d 100644
--- a/release/scripts/startup/bl_ui/properties_view_layer.py
+++ b/release/scripts/startup/bl_ui/properties_view_layer.py
@@ -113,8 +113,7 @@ class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_glossy_color", text="Color")
col = layout.column(heading="Volume", align=True)
- col.prop(view_layer_eevee, "use_pass_volume_transmittance", text="Transmittance")
- col.prop(view_layer_eevee, "use_pass_volume_scatter", text="Scatter")
+ col.prop(view_layer_eevee, "use_pass_volume_direct", text="Light")
col = layout.column(heading="Other", align=True)
col.prop(view_layer, "use_pass_emit", text="Emission")
@@ -187,8 +186,8 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel):
col.prop(view_layer, "use_pass_cryptomatte_material", text="Material")
col.prop(view_layer, "use_pass_cryptomatte_asset", text="Asset")
col = layout.column()
- col.active = any((view_layer.use_pass_cryptomatte_object,
- view_layer.use_pass_cryptomatte_material,
+ col.active = any((view_layer.use_pass_cryptomatte_object,
+ view_layer.use_pass_cryptomatte_material,
view_layer.use_pass_cryptomatte_asset))
col.prop(view_layer, "pass_cryptomatte_depth", text="Levels")
col.prop(view_layer, "use_pass_cryptomatte_accurate", text="Accurate Mode")
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index a9bb2e79762..98b155d8a21 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -17,27 +17,79 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
+
+import bpy
+
from bpy.types import Header, Panel, Menu, UIList
+from bpy_extras import (
+ asset_utils,
+)
+
class FILEBROWSER_HT_header(Header):
bl_space_type = 'FILE_BROWSER'
+ def draw_asset_browser_buttons(self, context):
+ layout = self.layout
+
+ space_data = context.space_data
+ params = space_data.params
+
+ row = layout.row(align=True)
+ row.prop(params, "asset_library", text="")
+ # External libraries don't auto-refresh, add refresh button.
+ if params.asset_library != 'LOCAL':
+ row.operator("file.refresh", text="", icon="FILE_REFRESH")
+
+ layout.separator_spacer()
+
+ # Uses prop_with_popover() as popover() only adds the triangle icon in headers.
+ layout.prop_with_popover(
+ params,
+ "display_type",
+ panel="FILEBROWSER_PT_display",
+ text="",
+ icon_only=True,
+ )
+ layout.prop_with_popover(
+ params,
+ "display_type",
+ panel="FILEBROWSER_PT_filter",
+ text="",
+ icon='FILTER',
+ icon_only=True,
+ )
+
+ layout.prop(params, "filter_search", text="", icon='VIEWZOOM')
+
+ layout.operator(
+ "screen.region_toggle",
+ text="",
+ icon='PREFERENCES',
+ depress=is_option_region_visible(context, space_data)
+ ).region_type = 'TOOL_PROPS'
+
def draw(self, context):
+ from bpy_extras.asset_utils import SpaceAssetInfo
+
layout = self.layout
- st = context.space_data
+ space_data = context.space_data
- if st.active_operator is None:
+ if space_data.active_operator is None:
layout.template_header()
FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout)
- # can be None when save/reload with a file selector open
-
- layout.separator_spacer()
+ if SpaceAssetInfo.is_asset_browser(space_data):
+ layout.separator()
+ self.draw_asset_browser_buttons(context)
+ else:
+ layout.separator_spacer()
- layout.template_running_jobs()
+ if not context.screen.show_statusbar:
+ layout.template_running_jobs()
class FILEBROWSER_PT_display(Panel):
@@ -144,6 +196,9 @@ class FILEBROWSER_PT_filter(Panel):
row.label(icon='BLANK1') # Indentation
sub = row.column(align=True)
+
+ sub.prop(params, "use_filter_asset_only")
+
filter_id = params.filter_id
for identifier in dir(filter_id):
if identifier.startswith("category_"):
@@ -160,6 +215,11 @@ def panel_poll_is_upper_region(region):
return region.alignment in {'LEFT', 'RIGHT'}
+def panel_poll_is_asset_browsing(context):
+ from bpy_extras.asset_utils import SpaceAssetInfo
+ return SpaceAssetInfo.is_asset_browser_poll(context)
+
+
class FILEBROWSER_UL_dir(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
direntry = item
@@ -187,7 +247,7 @@ class FILEBROWSER_PT_bookmarks_volumes(Panel):
@classmethod
def poll(cls, context):
- return panel_poll_is_upper_region(context.region)
+ return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context)
def draw(self, context):
layout = self.layout
@@ -207,7 +267,11 @@ class FILEBROWSER_PT_bookmarks_system(Panel):
@classmethod
def poll(cls, context):
- return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region)
+ return (
+ not context.preferences.filepaths.hide_system_bookmarks and
+ panel_poll_is_upper_region(context.region) and
+ not panel_poll_is_asset_browsing(context)
+ )
def draw(self, context):
layout = self.layout
@@ -241,7 +305,10 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel):
@classmethod
def poll(cls, context):
- return panel_poll_is_upper_region(context.region)
+ return (
+ panel_poll_is_upper_region(context.region) and
+ not panel_poll_is_asset_browsing(context)
+ )
def draw(self, context):
layout = self.layout
@@ -278,7 +345,11 @@ class FILEBROWSER_PT_bookmarks_recents(Panel):
@classmethod
def poll(cls, context):
- return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region)
+ return (
+ not context.preferences.filepaths.hide_recent_locations and
+ panel_poll_is_upper_region(context.region) and
+ not panel_poll_is_asset_browsing(context)
+ )
def draw(self, context):
layout = self.layout
@@ -302,7 +373,11 @@ class FILEBROWSER_PT_advanced_filter(Panel):
@classmethod
def poll(cls, context):
# only useful in append/link (library) context currently...
- return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region)
+ return (
+ context.space_data.params.use_library_browsing and
+ panel_poll_is_upper_region(context.region) and
+ not panel_poll_is_asset_browsing(context)
+ )
def draw(self, context):
layout = self.layout
@@ -314,12 +389,26 @@ class FILEBROWSER_PT_advanced_filter(Panel):
if params.use_filter_blendid:
layout.separator()
col = layout.column(align=True)
+
+ col.prop(params, "use_filter_asset_only")
+
filter_id = params.filter_id
for identifier in dir(filter_id):
if identifier.startswith("filter_"):
col.prop(filter_id, identifier, toggle=True)
+def is_option_region_visible(context, space):
+ if not space.active_operator:
+ return False
+
+ for region in context.area.regions:
+ if region.type == 'TOOL_PROPS' and region.width <= 1:
+ return False
+
+ return True
+
+
class FILEBROWSER_PT_directory_path(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'UI'
@@ -334,16 +423,6 @@ class FILEBROWSER_PT_directory_path(Panel):
return True
- def is_option_region_visible(self, context, space):
- if not space.active_operator:
- return False
-
- for region in context.area.regions:
- if region.type == 'TOOL_PROPS' and region.width <= 1:
- return False
-
- return True
-
def draw(self, context):
layout = self.layout
space = context.space_data
@@ -388,7 +467,7 @@ class FILEBROWSER_PT_directory_path(Panel):
"screen.region_toggle",
text="",
icon='PREFERENCES',
- depress=self.is_option_region_visible(context, space)
+ depress=is_option_region_visible(context, space)
).region_type = 'TOOL_PROPS'
@@ -482,6 +561,99 @@ class FILEBROWSER_MT_context_menu(Menu):
layout.prop_menu_enum(params, "sort_method")
+class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel):
+ bl_label = "Asset Navigation"
+ bl_region_type = 'TOOLS'
+ bl_options = {'HIDE_HEADER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ space_file = context.space_data
+
+ col = layout.column()
+
+ col.scale_x = 1.3
+ col.scale_y = 1.3
+ col.prop(space_file.params, "asset_category", expand=True)
+
+
+class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel):
+ bl_region_type = 'TOOL_PROPS'
+ bl_label = "Asset Metadata"
+ bl_options = {'HIDE_HEADER'}
+
+ def draw(self, context):
+ layout = self.layout
+ active_file = context.active_file
+ active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ if not active_file or not active_asset:
+ 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="")
+
+
+class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel):
+ bl_label = "Preview"
+
+ def draw(self, context):
+ layout = self.layout
+ active_file = context.active_file
+
+ row = layout.row()
+ box = row.box()
+ box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0)
+ if bpy.ops.ed.lib_id_load_custom_preview.poll():
+ col = row.column(align=True)
+ col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="")
+ col.separator()
+ col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="")
+
+
+class ASSETBROWSER_PT_metadata_details(asset_utils.AssetMetaDataPanel, Panel):
+ bl_label = "Details"
+
+ def draw(self, context):
+ layout = self.layout
+ active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ layout.use_property_split = True
+
+ if active_asset:
+ layout.prop(active_asset, "description")
+
+
+class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel):
+ bl_label = "Tags"
+
+ def draw(self, context):
+ layout = self.layout
+ asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context)
+
+ row = layout.row()
+ row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags",
+ asset_data, "active_tag", rows=4)
+
+ col = row.column(align=True)
+ col.operator("asset.tag_add", icon='ADD', text="")
+ col.operator("asset.tag_remove", icon='REMOVE', text="")
+
+
+class ASSETBROWSER_UL_metadata_tags(UIList):
+ def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
+ tag = item
+
+ row = layout.row(align=True)
+ # Non-editable entries would show grayed-out, which is bad in this specific case, so switch to mere label.
+ if tag.is_property_readonly("name"):
+ row.label(text=tag.name, icon_value=icon)
+ else:
+ row.prop(tag, "name", text="", emboss=False, icon_value=icon)
+
+
classes = (
FILEBROWSER_HT_header,
FILEBROWSER_PT_display,
@@ -498,6 +670,12 @@ classes = (
FILEBROWSER_MT_view,
FILEBROWSER_MT_select,
FILEBROWSER_MT_context_menu,
+ ASSETBROWSER_PT_navigation_bar,
+ ASSETBROWSER_PT_metadata,
+ ASSETBROWSER_PT_metadata_preview,
+ ASSETBROWSER_PT_metadata_details,
+ ASSETBROWSER_PT_metadata_tags,
+ ASSETBROWSER_UL_metadata_tags,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index f4e88b70281..342b72acb8c 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -1465,7 +1465,7 @@ class IMAGE_PT_overlay(Panel):
bl_ui_units_x = 13
def draw(self, context):
- pass
+ pass
class IMAGE_PT_overlay_uv_edit(Panel):
@@ -1496,7 +1496,6 @@ class IMAGE_PT_overlay_uv_edit(Panel):
subrow.prop(uvedit, "display_stretch_type", text="")
-
class IMAGE_PT_overlay_uv_edit_geometry(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
@@ -1529,7 +1528,6 @@ class IMAGE_PT_overlay_uv_edit_geometry(Panel):
row.prop(uvedit, "show_faces", text="Faces")
-
class IMAGE_PT_overlay_texture_paint(Panel):
bl_space_type = 'IMAGE_EDITOR'
bl_region_type = 'HEADER'
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index 1c4d1919f43..e6af83b61f4 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -168,7 +168,6 @@ class NODE_HT_header(Header):
else:
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
-
else:
# Custom node tree is edited as independent ID block
NODE_MT_editor_menus.draw_collapsible(context, layout)
diff --git a/release/scripts/startup/bl_ui/space_properties.py b/release/scripts/startup/bl_ui/space_properties.py
index 0f64ab63d6b..765cab1ace2 100644
--- a/release/scripts/startup/bl_ui/space_properties.py
+++ b/release/scripts/startup/bl_ui/space_properties.py
@@ -61,8 +61,10 @@ class PROPERTIES_PT_navigation_bar(Panel):
layout.scale_x = 1.4
layout.scale_y = 1.4
if view.search_filter:
- layout.prop_tabs_enum(view, "context", data_highlight=view,
- property_highlight="tab_search_results", icon_only=True)
+ layout.prop_tabs_enum(
+ view, "context", data_highlight=view,
+ property_highlight="tab_search_results", icon_only=True,
+ )
else:
layout.prop_tabs_enum(view, "context", icon_only=True)
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 3f92fce81f6..e79b5d3e2c8 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -129,6 +129,8 @@ class SEQUENCER_HT_header(Header):
layout = self.layout
st = context.space_data
+ scene = context.scene
+ sequencer_tool_settings = context.tool_settings.sequencer_tool_settings
show_region_tool_header = st.show_region_tool_header
@@ -139,12 +141,17 @@ class SEQUENCER_HT_header(Header):
SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
- if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
-
+ if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ layout.separator_spacer()
+ row = layout.row(align=True)
+ row.prop(sequencer_tool_settings, "fit_method", text="")
layout.separator_spacer()
- layout.prop(st, "display_mode", text="", icon_only=True)
+ if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
+ if st.view_type == 'PREVIEW':
+ layout.separator_spacer()
+ layout.prop(st, "display_mode", text="", icon_only=True)
layout.prop(st, "preview_channels", text="", icon_only=True)
gpd = context.gpencil_data
@@ -157,6 +164,12 @@ class SEQUENCER_HT_header(Header):
if tool_settings.use_proportional_edit:
row.prop(tool_settings, "proportional_edit_falloff", icon_only=True)
+ row = layout.row(align=True)
+ row.prop(st, "show_strip_overlay", text="", icon='OVERLAY')
+ sub = row.row(align=True)
+ sub.popover(panel="SEQUENCER_PT_overlay", text="")
+ sub.active = st.show_strip_overlay
+
class SEQUENCER_MT_editor_menus(Menu):
bl_idname = "SEQUENCER_MT_editor_menus"
@@ -176,6 +189,80 @@ class SEQUENCER_MT_editor_menus(Menu):
layout.menu("SEQUENCER_MT_strip")
+class SEQUENCER_PT_overlay(Panel):
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'HEADER'
+ bl_label = "Overlays"
+ bl_ui_units_x = 7
+
+ def draw(self, _context):
+ pass
+
+
+class SEQUENCER_PT_overlay(Panel):
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'HEADER'
+ bl_label = "Overlays"
+ bl_ui_units_x = 7
+
+ def draw(self, _context):
+ pass
+
+
+class SEQUENCER_PT_preview_overlay(Panel):
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'HEADER'
+ bl_parent_id = 'SEQUENCER_PT_overlay'
+ bl_label = "Preview Overlays"
+
+ @classmethod
+ def poll(cls, context):
+ st = context.space_data
+ return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} and st.display_mode == 'IMAGE'
+
+ def draw(self, context):
+ ed = context.scene.sequence_editor
+ st = context.space_data
+ layout = self.layout
+
+ layout.active = st.show_strip_overlay
+ layout.prop(ed, "show_overlay", text="Frame Overlay")
+ layout.prop(st, "show_safe_areas", text="Safe Areas")
+ layout.prop(st, "show_metadata", text="Metadata")
+ layout.prop(st, "show_annotation", text="Annotations")
+
+
+class SEQUENCER_PT_sequencer_overlay(Panel):
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'HEADER'
+ bl_parent_id = 'SEQUENCER_PT_overlay'
+ bl_label = "Sequencer Overlays"
+
+ @classmethod
+ def poll(cls, context):
+ st = context.space_data
+ return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
+
+ def draw(self, context):
+ st = context.space_data
+ layout = self.layout
+
+ layout.active = st.show_strip_overlay
+
+ layout.prop(st, "show_strip_name", text="Name")
+ layout.prop(st, "show_strip_source", text="Source")
+ layout.prop(st, "show_strip_duration", text="Duration")
+
+ layout.separator()
+
+ layout.prop(st, "show_strip_offset", text="Offsets")
+ layout.prop(st, "show_fcurves", text="F-Curves")
+
+ layout.separator()
+
+ layout.prop_menu_enum(st, "waveform_display_type")
+
+
class SEQUENCER_MT_view_cache(Menu):
bl_label = "Cache"
@@ -294,6 +381,12 @@ class SEQUENCER_MT_view(Menu):
layout.operator("view2d.zoom_border", text="Zoom")
layout.menu("SEQUENCER_MT_preview_zoom")
+ if st.display_mode == 'IMAGE':
+ layout.prop(st, "use_zoom_to_fit")
+ elif st.display_mode == 'WAVEFORM':
+ layout.separator()
+ layout.prop(st, "show_separate_color", text="Show Separate Color Channels")
+
layout.separator()
layout.menu("SEQUENCER_MT_proxy")
@@ -318,22 +411,8 @@ class SEQUENCER_MT_view(Menu):
layout.separator()
layout.prop(st, "show_seconds")
- layout.prop(st, "show_strip_offset")
- layout.prop(st, "show_fcurves")
layout.prop(st, "show_markers")
layout.menu("SEQUENCER_MT_view_cache", text="Show Cache")
- layout.prop_menu_enum(st, "waveform_display_type", text="Show Waveforms")
-
- if is_preview:
- layout.separator()
- if st.display_mode == 'IMAGE':
- layout.prop(st, "use_zoom_to_fit")
- layout.prop(ed, "show_overlay", text="Show Frame Overlay")
- layout.prop(st, "show_safe_areas", text="Show Safe Areas")
- layout.prop(st, "show_metadata", text="Show Metadata")
- layout.prop(st, "show_annotation", text="Show Annotations")
- elif st.display_mode == 'WAVEFORM':
- layout.prop(st, "show_separate_color", text="Show Separate Color Channels")
layout.separator()
@@ -629,6 +708,22 @@ class SEQUENCER_MT_add_effect(Menu):
col.enabled = selected_sequences_len(context) != 0
+class SEQUENCER_MT_strip_image_transform(Menu):
+ bl_label = "Image Transform"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT'
+ layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL'
+ layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH'
+ layout.separator()
+
+ layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION'
+ layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE'
+ layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION'
+ layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL'
+
class SEQUENCER_MT_strip_transform(Menu):
bl_label = "Transform"
@@ -723,6 +818,7 @@ class SEQUENCER_MT_strip(Menu):
layout.separator()
layout.menu("SEQUENCER_MT_strip_transform")
+ layout.menu("SEQUENCER_MT_strip_image_transform")
layout.separator()
layout.operator("sequencer.split", text="Split").type = 'SOFT'
@@ -2214,6 +2310,7 @@ classes = (
SEQUENCER_MT_strip_effect,
SEQUENCER_MT_strip_movie,
SEQUENCER_MT_strip,
+ SEQUENCER_MT_strip_image_transform,
SEQUENCER_MT_strip_transform,
SEQUENCER_MT_strip_input,
SEQUENCER_MT_strip_lock_mute,
@@ -2222,6 +2319,10 @@ classes = (
SEQUENCER_PT_active_tool,
SEQUENCER_PT_strip,
+ SEQUENCER_PT_overlay,
+ SEQUENCER_PT_preview_overlay,
+ SEQUENCER_PT_sequencer_overlay,
+
SEQUENCER_PT_effect,
SEQUENCER_PT_scene,
SEQUENCER_PT_mask,
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 16b02db9377..3b9ce5311f3 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -297,6 +297,7 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel):
col.label(text="New Keyframe Type")
col.prop(tool_settings, "keyframe_type", text="")
+
class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel):
bl_label = "Auto Keyframing"
bl_options = {'HIDE_HEADER'}
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index ca5e6404159..0411d5c64bc 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -360,7 +360,7 @@ class _defs_transform:
idname="builtin.transform",
label="Transform",
description=(
- "Supports any combination of grab, rotate & scale at once"
+ "Supports any combination of grab, rotate, and scale at once"
),
icon="ops.transform.transform",
widget="VIEW3D_GGT_xform_gizmo",
@@ -1654,7 +1654,7 @@ class _defs_image_uv_transform:
idname="builtin.transform",
label="Transform",
description=(
- "Supports any combination of grab, rotate & scale at once"
+ "Supports any combination of grab, rotate, and scale at once"
),
icon="ops.transform.transform",
widget="IMAGE_GGT_gizmo2d",
@@ -2903,10 +2903,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_sequencer_generic.blade,
],
'SEQUENCER_PREVIEW': [
- _defs_sequencer_generic.sample,
*_tools_select,
- *_tools_annotate,
_defs_sequencer_generic.blade,
+ _defs_sequencer_generic.sample,
+ *_tools_annotate,
],
}
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 761e60aef30..0167f0b1e20 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -207,11 +207,11 @@ class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel):
col.prop(view, "ui_line_width", text="Line Width")
col.prop(view, "show_splash", text="Splash Screen")
col.prop(view, "show_developer_ui")
-
+
col.separator()
col = layout.column(heading="Tooltips", align=True)
- col.prop(view, "show_tooltips", text = "User Tooltips")
+ col.prop(view, "show_tooltips", text="User Tooltips")
sub = col.column()
sub.active = view.show_tooltips
sub.prop(view, "show_tooltips_python")
@@ -1339,6 +1339,40 @@ class USERPREF_PT_saveload_autorun(FilePathsPanel, Panel):
row.operator("preferences.autoexec_path_remove", text="", icon='X', emboss=False).index = i
+class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel):
+ bl_label = "Asset Libraries"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = False
+ layout.use_property_decorate = False
+
+ paths = context.preferences.filepaths
+
+ box = layout.box()
+ split = box.split(factor=0.35)
+ name_col = split.column()
+ path_col = split.column()
+
+ row = name_col.row(align=True) # Padding
+ row.separator()
+ row.label(text="Name")
+
+ row = path_col.row(align=True) # Padding
+ row.separator()
+ row.label(text="Path")
+
+
+ for i, library in enumerate(paths.asset_libraries):
+ name_col.prop(library, "name", text="")
+ row = path_col.row()
+ row.prop(library, "path", text="")
+ row.operator("preferences.asset_library_remove", text="", icon='X', emboss=False).index = i
+ row = box.row()
+ row.alignment = 'LEFT'
+ row.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False)
+
+
# -----------------------------------------------------------------------------
# Save/Load Panels
@@ -1543,8 +1577,6 @@ class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel):
col.prop(inputs, "use_zoom_to_mouse")
col.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction")
- # sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
-
class USERPREF_PT_navigation_fly_walk(NavigationPanel, CenterAlignMixIn, Panel):
bl_label = "Fly & Walk"
@@ -1850,12 +1882,12 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
is_visible = is_visible and is_enabled
if is_visible:
- if search and search not in info["name"].lower():
- if info["author"]:
- if search not in info["author"].lower():
- continue
- else:
- continue
+ if search and not (
+ (search in info["name"].lower()) or
+ (info["author"] and (search in info["author"].lower())) or
+ ((filter == "All") and (search in info["category"].lower()))
+ ):
+ continue
# Skip 2.7x add-ons included with Blender, unless in debug mode.
is_addon_27x = info.get("blender", (0,)) < (2, 80)
@@ -2204,6 +2236,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
self._draw_items(
context, (
({"property": "use_new_hair_type"}, "T68981"),
+ ({"property": "use_new_point_cloud_type"}, "T75717"),
),
)
@@ -2289,6 +2322,7 @@ classes = (
USERPREF_PT_file_paths_render,
USERPREF_PT_file_paths_applications,
USERPREF_PT_file_paths_development,
+ USERPREF_PT_file_paths_asset_libraries,
USERPREF_PT_saveload_blend,
USERPREF_PT_saveload_blend_autosave,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 10c9f25b92a..8b5183faf62 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2147,7 +2147,8 @@ class VIEW3D_MT_add(Menu):
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
if context.preferences.experimental.use_new_hair_type:
layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR')
- layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
+ if context.preferences.experimental.use_new_point_cloud_type:
+ layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME')
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
@@ -5305,6 +5306,7 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu):
op.filter_type = 'CONTRAST_DECREASE'
op.auto_iteration_count = False
+
class VIEW3D_MT_sculpt_automasking_pie(Menu):
bl_label = "Automasking"
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 293d55a6015..577f9678a62 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -966,6 +966,7 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel):
row.active = mesh.use_mirror_vertex_group_x
row.prop(mesh, "use_mirror_topology")
+
class VIEW3D_PT_tools_weightpaint_symmetry_for_topbar(Panel):
bl_space_type = 'TOPBAR'
bl_region_type = 'HEADER'
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index abab50b95a2..c3ab1b3db97 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -481,8 +481,11 @@ def not_implemented_node(idname):
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
- NodeItem("GeometryNodeRandomAttribute"),
+ NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
+ NodeItem("GeometryNodeAttributeFill"),
+ NodeItem("GeometryNodeAttributeMix"),
+ NodeItem("GeometryNodeAttributeColorRamp"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
diff --git a/release/steam/README.md b/release/steam/README.md
new file mode 100644
index 00000000000..05eda799c3f
--- /dev/null
+++ b/release/steam/README.md
@@ -0,0 +1,70 @@
+Creating Steam builds for Blender
+=================================
+
+This script automates creation of the Steam files: download of the archives,
+extraction of the archives, preparation of the build scripts (VDF files), actual
+building of the Steam game files.
+
+Requirements
+============
+
+* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG
+ archive did not work Windows nor on Linux using 7-zip. All DMG archives tested
+ failed to be extracted. As such only MacOS is known to work.
+* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the
+ Steam game files. The path to the `steamcmd` is what is actually needed.
+* SteamWorks credentials - Needed to log in using `steamcmd`.
+* Login to SteamWorks with the `steamcmd` from the command-line at least once -
+ Needded to ensure the user is properly logged in. On a new machine the user
+ will have to go through two-factor authentication.
+* App ID and Depot IDs - Needed to create the VDF files.
+* Python 3.x - 3.7 was tested.
+* Base URL - for downloading the archives.
+
+Usage
+=====
+
+```bash
+$ export STEAMUSER=SteamUserName
+$ export STEAMPW=SteamUserPW
+$ export BASEURL=https://download.blender.org/release/Blender2.83/
+$ export VERSION=2.83.3
+$ export APPID=appidnr
+$ export WINID=winidnr
+$ export LINID=linuxidnr
+$ export MACOSID=macosidnr
+
+# log in to SteamWorks from command-line at least once
+
+$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW
+
+# once that has been done we can now actually start our tool
+
+$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd
+```
+
+All arguments in the above example are required.
+
+At the start the tool will login using `steamcmd`. This is necessary to let the
+Steam SDK update itself if necessary.
+
+There are a few optional arguments:
+
+* `--dryrun`: If set building the game files will not actually happen. A set of
+ log files and a preview manifest per depot will be created in the output folder.
+ This can be used to double-check everything works as expected.
+* `--skipdl`: If set will skip downloading of the archives. The tool expects the
+ archives to already exist in the correct content location.
+* `--skipextract`: If set will skip extraction of all archives. The tool expects
+ the archives to already have been correctly extracted in the content location.
+
+Run the tool with `-h` for detailed information on each argument.
+
+The content and output folders are generated through appending the version
+without dots to the words `content` and `output` respectively, e.g. `content2833`
+and `output2833`. These folders are created next to the tool.
+
+From all `.template` files the Steam build scripts will be generated also in the
+same directory as the tool. The files will have the extension `.vdf`.
+
+In case of errors the tool will have a non-zero return code. \ No newline at end of file
diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template
new file mode 100644
index 00000000000..9e2d0625d72
--- /dev/null
+++ b/release/steam/blender_app_build.vdf.template
@@ -0,0 +1,17 @@
+"appbuild"
+{
+ "appid" "[APPID]"
+ "desc" "Blender [VERSION]" // description for this build
+ "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file
+ "contentroot" "./[CONTENT]" // root content folder, relative to location of this file
+ "setlive" "" // branch to set live after successful build, non if empty
+ "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe
+ "local" "" // set to flie path of local content server
+
+ "depots"
+ {
+ "[WINID]" "depot_build_win.vdf"
+ "[LINUXID]" "depot_build_linux.vdf"
+ "[MACOSID]" "depot_build_macos.vdf"
+ }
+}
diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py
new file mode 100644
index 00000000000..2ecd0c347f7
--- /dev/null
+++ b/release/steam/create_steam_builds.py
@@ -0,0 +1,397 @@
+#!/usr/bin/env python3
+
+import argparse
+import pathlib
+import requests
+import shutil
+import subprocess
+from typing import Callable, Iterator, List, Tuple
+
+# supported archive and platform endings, used to create actual archive names
+archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"]
+
+
+def add_optional_argument(option: str, help: str) -> None:
+ global parser
+ """Add an optional argument
+
+ Args:
+ option (str): Option to add
+ help (str): Help description for the argument
+ """
+ parser.add_argument(option, help=help, action='store_const', const=1)
+
+
+def blender_archives(version: str) -> Iterator[str]:
+ """Generator for Blender archives for version.
+
+ Yields for items in archive_endings an archive name in the form of
+ blender-{version}-{ending}.
+
+ Args:
+ version (str): Version string of the form 2.83.2
+
+
+ Yields:
+ Iterator[str]: Name in the form of blender-{version}-{ending}
+ """
+ global archive_endings
+
+ for ending in archive_endings:
+ yield f"blender-{version}-{ending}"
+
+
+def get_archive_type(archive_type: str, version: str) -> str:
+ """Return the archive of given type and version.
+
+ Args:
+ archive_type (str): extension for archive type to check for
+ version (str): Version string in the form 2.83.2
+
+ Raises:
+ Exception: Execption when archive type isn't found
+
+ Returns:
+ str: archive name for given type
+ """
+
+ for archive in blender_archives(version):
+ if archive.endswith(archive_type):
+ return archive
+ raise Exception("Unknown archive type")
+
+
+def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str:
+ """Execute the given command.
+
+ Returns the process stdout upon success if any.
+
+ On error print message the command with name that has failed. Print stdout
+ and stderr of the process if any, and then exit with given error code.
+
+ Args:
+ cmd (List[str]): Command in list format, each argument as their own item
+ name (str): Name of command to use when printing to command-line
+ errcode (int): Error code to use in case of exit()
+ cwd (str, optional): Folder to use as current work directory for command
+ execution. Defaults to ".".
+ capture_output (bool, optional): Whether to capture command output or not.
+ Defaults to True.
+
+ Returns:
+ str: stdout if any, or empty string
+ """
+ cmd_process = subprocess.run(
+ cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd)
+ if cmd_process.returncode == 0:
+ if cmd_process.stdout:
+ return cmd_process.stdout
+ else:
+ return ""
+ else:
+ print(f"ERROR: {name} failed.")
+ if cmd_process.stdout:
+ print(cmd_process.stdout)
+ if cmd_process.stderr:
+ print(cmd_process.stderr)
+ exit(errcode)
+ return ""
+
+
+def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path):
+ """Download archives from the given base_url.
+
+ Archives is a generator for Blender archive names based on version.
+
+ Archive names are appended to the base_url to load from, and appended to
+ dst_dir to save to.
+
+ Args:
+ base_url (str): Base URL to load archives from
+ archives (Callable[[str], Iterator[str]]): Generator for Blender archive
+ names based on version
+ version (str): Version string in the form of 2.83.2
+ dst_dir (pathlib.Path): Download destination
+ """
+
+ if base_url[-1] != '/':
+ base_url = base_url + '/'
+
+ for archive in archives(version):
+ download_url = f"{base_url}{archive}"
+ target_file = dst_dir.joinpath(archive)
+ download_file(download_url, target_file)
+
+
+def download_file(from_url: str, to_file: pathlib.Path) -> None:
+ """Download from_url as to_file.
+
+ Actual downloading will be skipped if --skipdl is given on the command-line.
+
+ Args:
+ from_url (str): Full URL to resource to download
+ to_file (pathlib.Path): Full path to save downloaded resource as
+ """
+ global args
+
+ if not args.skipdl or not to_file.exists():
+ print(f"Downloading {from_url}")
+ with open(to_file, "wb") as download_zip:
+ response = requests.get(from_url)
+ if response.status_code != requests.codes.ok:
+ print(f"ERROR: failed to download {from_url} (status code: {response.status_code})")
+ exit(1313)
+ download_zip.write(response.content)
+ else:
+ print(f"Downloading {from_url} skipped")
+ print(" ... OK")
+
+
+def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None:
+ """Copy the contents of the given DMG file to the destination folder.
+
+ Args:
+ dmg_file (pathlib.Path): Full path to DMG archive to extract from
+ dst (pathlib.Path): Full path to destination to extract to
+ """
+ hdiutil_attach = ["hdiutil",
+ "attach",
+ "-readonly",
+ f"{dmg_file}"
+ ]
+ attached = execute_command(hdiutil_attach, "hdiutil attach", 1)
+
+ # Last line of output is what we want, it is of the form
+ # /dev/somedisk Apple_HFS /Volumes/Blender
+ # We want to retain the mount point, and the folder the mount is
+ # created on. The mounted disk we need for detaching, the folder we
+ # need to be able to copy the contents to where we can use them
+ attachment_items = attached.splitlines()[-1].split()
+ mounted_disk = attachment_items[0]
+ source_location = pathlib.Path(attachment_items[2], "Blender.app")
+
+ print(f"{source_location} -> {dst}")
+
+ shutil.copytree(source_location, dst)
+
+ hdiutil_detach = ["hdiutil",
+ "detach",
+ f"{mounted_disk}"
+ ]
+ execute_command(hdiutil_detach, "hdiutil detach", 2)
+
+
+def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path:
+ """
+ Create the Steam build script
+
+ Use the given template and template variable tuple list.
+
+ Returns pathlib.Path to the created script.
+
+ Args:
+ template_name (str): [description]
+ vars (List[Tuple[str, str]]): [description]
+
+ Returns:
+ pathlib.Path: Full path to the generated script
+ """
+ build_script = pathlib.Path(".", template_name).read_text()
+ for var in vars:
+ build_script = build_script.replace(var[0], var[1])
+ build_script_file = template_name.replace(".template", "")
+ build_script_path = pathlib.Path(".", build_script_file)
+ build_script_path.write_text(build_script)
+ return build_script_path
+
+
+def clean_up() -> None:
+ """Remove intermediate files depending on given command-line arguments
+ """
+ global content_location, args
+
+ if not args.leavearch and not args.leaveextracted:
+ shutil.rmtree(content_location)
+
+ if args.leavearch and not args.leaveextracted:
+ shutil.rmtree(content_location.joinpath(zip_extract_folder))
+ shutil.rmtree(content_location.joinpath(tarxz_extract_folder))
+ shutil.rmtree(content_location.joinpath(dmg_extract_folder))
+
+ if args.leaveextracted and not args.leavearch:
+ import os
+ os.remove(content_location.joinpath(zipped_blender))
+ os.remove(content_location.joinpath(tarxz_blender))
+ os.remove(content_location.joinpath(dmg_blender))
+
+
+def extract_archive(archive: str, extract_folder_name: str,
+ cmd: List[str], errcode: int) -> None:
+ """Extract all files from archive to given folder name.
+
+ Will not extract if
+ target folder already exists, or if --skipextract was given on the
+ command-line.
+
+ Args:
+ archive (str): Archive name to extract
+ extract_folder_name (str): Folder name to extract to
+ cmd (List[str]): Command with arguments to use
+ errcode (int): Error code to use for exit()
+ """
+ global args, content_location
+
+ extract_location = content_location.joinpath(extract_folder_name)
+
+ pre_extract = set(content_location.glob("*"))
+
+ if not args.skipextract or not extract_location.exists():
+ print(f"Extracting files from {archive}...")
+ cmd.append(content_location.joinpath(archive))
+ execute_command(cmd, cmd[0], errcode, cwd=content_location)
+ # in case we use a non-release archive the naming will be incorrect.
+ # simply rename to expected target name
+ post_extract = set(content_location.glob("*"))
+ diff_extract = post_extract - pre_extract
+ if not extract_location in diff_extract:
+ folder_to_rename = list(diff_extract)[0]
+ folder_to_rename.rename(extract_location)
+ print(" OK")
+ else:
+ print(f"Skipping extraction {archive}!")
+
+# ==============================================================================
+
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("--baseurl", required=True,
+ help="The base URL for files to download, "
+ "i.e. https://download.blender.org/release/Blender2.83/")
+
+parser.add_argument("--version", required=True,
+ help="The Blender version to release, in the form 2.83.3")
+
+parser.add_argument("--appid", required=True,
+ help="The Blender App ID on Steam")
+parser.add_argument("--winid", required=True,
+ help="The Windows depot ID")
+parser.add_argument("--linuxid", required=True,
+ help="The Linux depot ID")
+parser.add_argument("--macosid", required=True,
+ help="The MacOS depot ID")
+
+parser.add_argument("--steamcmd", required=True,
+ help="Path to the steamcmd")
+parser.add_argument("--steamuser", required=True,
+ help="The login for the Steam builder user")
+parser.add_argument("--steampw", required=True,
+ help="Login password for the Steam builder user")
+
+add_optional_argument("--dryrun",
+ "If set the Steam files will not be uploaded")
+add_optional_argument("--leavearch",
+ help="If set don't clean up the downloaded archives")
+add_optional_argument("--leaveextracted",
+ help="If set don't clean up the extraction folders")
+add_optional_argument("--skipdl",
+ help="If set downloading the archives is skipped if it already exists locally.")
+add_optional_argument("--skipextract",
+ help="If set skips extracting of archives. The tool assumes the archives"
+ "have already been extracted to their correct locations")
+
+args = parser.parse_args()
+
+VERSIONNODOTS = args.version.replace('.', '')
+OUTPUT = f"output{VERSIONNODOTS}"
+CONTENT = f"content{VERSIONNODOTS}"
+
+# ===== set up main locations
+
+content_location = pathlib.Path(".", CONTENT).absolute()
+output_location = pathlib.Path(".", OUTPUT).absolute()
+
+content_location.mkdir(parents=True, exist_ok=True)
+output_location.mkdir(parents=True, exist_ok=True)
+
+# ===== login
+
+# Logging into Steam once to ensure the SDK updates itself properly. If we don't
+# do that the combined +login and +run_app_build_http at the end of the tool
+# will fail.
+steam_login = [args.steamcmd,
+ "+login",
+ args.steamuser,
+ args.steampw,
+ "+quit"
+ ]
+print("Logging in to Steam...")
+execute_command(steam_login, "Login to Steam", 10)
+print(" OK")
+
+# ===== prepare Steam build scripts
+
+template_vars = [
+ ("[APPID]", args.appid),
+ ("[OUTPUT]", OUTPUT),
+ ("[CONTENT]", CONTENT),
+ ("[VERSION]", args.version),
+ ("[WINID]", args.winid),
+ ("[LINUXID]", args.linuxid),
+ ("[MACOSID]", args.macosid),
+ ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0")
+]
+
+blender_app_build = create_build_script(
+ "blender_app_build.vdf.template", template_vars)
+create_build_script("depot_build_win.vdf.template", template_vars)
+create_build_script("depot_build_linux.vdf.template", template_vars)
+create_build_script("depot_build_macos.vdf.template", template_vars)
+
+# ===== download archives
+
+download_archives(args.baseurl, blender_archives,
+ args.version, content_location)
+
+# ===== set up file and folder names
+
+zipped_blender = get_archive_type("zip", args.version)
+zip_extract_folder = zipped_blender.replace(".zip", "")
+tarxz_blender = get_archive_type("tar.xz", args.version)
+tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "")
+dmg_blender = get_archive_type("dmg", args.version)
+dmg_extract_folder = dmg_blender.replace(".dmg", "")
+
+# ===== extract
+
+unzip_cmd = ["unzip", "-q"]
+extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3)
+
+untarxz_cmd = ["tar", "-xf"]
+extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4)
+
+if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists():
+ print("Extracting files from Blender MacOS archive...")
+ blender_dmg = content_location.joinpath(dmg_blender)
+ target_location = content_location.joinpath(
+ dmg_extract_folder, "Blender.app")
+ copy_contents_from_dmg_to_path(blender_dmg, target_location)
+ print(" OK")
+else:
+ print("Skipping extraction of .dmg!")
+
+# ===== building
+
+print("Build Steam game files...")
+steam_build = [args.steamcmd,
+ "+login",
+ args.steamuser,
+ args.steampw,
+ "+run_app_build_http",
+ blender_app_build.absolute(),
+ "+quit"
+ ]
+execute_command(steam_build, "Build with steamcmd", 13)
+print(" OK")
+
+clean_up()
diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template
new file mode 100644
index 00000000000..0f69008548e
--- /dev/null
+++ b/release/steam/depot_build_linux.vdf.template
@@ -0,0 +1,31 @@
+"DepotBuildConfig"
+{
+ // Set your assigned depot ID here
+ "DepotID" "[LINUXID]"
+
+ // Set a root for all content.
+ // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
+ // will be resolved relative to this root.
+ // If you don't define ContentRoot, then it will be assumed to be
+ // the location of this script file, which probably isn't what you want
+ "ContentRoot" "./blender-[VERSION]-linux64/"
+
+ // include all files recursivley
+ "FileMapping"
+ {
+ // This can be a full path, or a path relative to ContentRoot
+ "LocalPath" "*"
+
+ // This is a path relative to the install folder of your game
+ "DepotPath" "."
+
+ // If LocalPath contains wildcards, setting this means that all
+ // matching files within subdirectories of LocalPath will also
+ // be included.
+ "recursive" "1"
+ }
+
+ // but exclude all symbol files
+ // This can be a full path, or a path relative to ContentRoot
+ "FileExclusion" "*.pdb"
+}
diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template
new file mode 100644
index 00000000000..33dde860462
--- /dev/null
+++ b/release/steam/depot_build_macos.vdf.template
@@ -0,0 +1,30 @@
+"DepotBuildConfig"
+{
+ // Set your assigned depot ID here
+ "DepotID" "[MACOSID]"
+
+ // Set a root for all content.
+ // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
+ // will be resolved relative to this root.
+ // If you don't define ContentRoot, then it will be assumed to be
+ // the location of this script file, which probably isn't what you want
+ "ContentRoot" "./blender-[VERSION]-macOS/"
+ // include all files recursivley
+ "FileMapping"
+ {
+ // This can be a full path, or a path relative to ContentRoot
+ "LocalPath" "*"
+
+ // This is a path relative to the install folder of your game
+ "DepotPath" "."
+
+ // If LocalPath contains wildcards, setting this means that all
+ // matching files within subdirectories of LocalPath will also
+ // be included.
+ "recursive" "1"
+ }
+
+ // but exclude all symbol files
+ // This can be a full path, or a path relative to ContentRoot
+ "FileExclusion" "*.pdb"
+}
diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template
new file mode 100644
index 00000000000..2c18a0f15dd
--- /dev/null
+++ b/release/steam/depot_build_win.vdf.template
@@ -0,0 +1,31 @@
+"DepotBuildConfig"
+{
+ // Set your assigned depot ID here
+ "DepotID" "[WINID]"
+
+ // Set a root for all content.
+ // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
+ // will be resolved relative to this root.
+ // If you don't define ContentRoot, then it will be assumed to be
+ // the location of this script file, which probably isn't what you want
+ "ContentRoot" "./blender-[VERSION]-windows64/"
+
+ // include all files recursivley
+ "FileMapping"
+ {
+ // This can be a full path, or a path relative to ContentRoot
+ "LocalPath" "*"
+
+ // This is a path relative to the install folder of your game
+ "DepotPath" "."
+
+ // If LocalPath contains wildcards, setting this means that all
+ // matching files within subdirectories of LocalPath will also
+ // be included.
+ "recursive" "1"
+ }
+
+ // but exclude all symbol files
+ // This can be a full path, or a path relative to ContentRoot
+ "FileExclusion" "*.pdb"
+}
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index da6df831c0a..efd30ba8509 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -23,7 +23,9 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_action_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_anim_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_asset_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_enums.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h
diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c
index 51d3849aa48..25235097505 100644
--- a/source/blender/blenfont/intern/blf_dir.c
+++ b/source/blender/blenfont/intern/blf_dir.c
@@ -47,6 +47,9 @@
#include "blf_internal.h"
#include "blf_internal_types.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+
static ListBase global_font_dir = {NULL, NULL};
static DirBLF *blf_dir_find(const char *path)
@@ -137,9 +140,11 @@ char *blf_dir_search(const char *file)
}
if (!s) {
- /* check the current directory, why not ? */
- if (BLI_exists(file)) {
- s = BLI_strdup(file);
+ /* Assume file is either an abslute path, or a relative path to current directory. */
+ BLI_strncpy(full_path, file, sizeof(full_path));
+ BLI_path_abs(full_path, BKE_main_blendfile_path(G_MAIN));
+ if (BLI_exists(full_path)) {
+ s = BLI_strdup(full_path);
}
}
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index d8605941974..717cfa607ad 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -30,10 +30,10 @@
extern "C" {
#endif
-struct BlendWriter;
struct BlendDataReader;
-struct BlendLibReader;
struct BlendExpander;
+struct BlendLibReader;
+struct BlendWriter;
struct bArmature;
/* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */
diff --git a/source/blender/blenkernel/BKE_anim_visualization.h b/source/blender/blenkernel/BKE_anim_visualization.h
index decb2e0b210..4e86abeed8d 100644
--- a/source/blender/blenkernel/BKE_anim_visualization.h
+++ b/source/blender/blenkernel/BKE_anim_visualization.h
@@ -26,14 +26,14 @@
extern "C" {
#endif
+struct BlendDataReader;
+struct BlendWriter;
struct Object;
struct ReportList;
struct Scene;
struct bAnimVizSettings;
struct bMotionPath;
struct bPoseChannel;
-struct BlendWriter;
-struct BlendDataReader;
/* ---------------------------------------------------- */
/* Animation Visualization */
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index e812d04c7d1..8d904bd6019 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -45,8 +45,6 @@ struct NlaKeyframingContext;
struct PathResolvedRNA;
struct PointerRNA;
struct PropertyRNA;
-struct ReportList;
-struct Scene;
struct bAction;
struct bActionGroup;
struct bContext;
diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h
index 6da6079ea2a..c9d671597e8 100644
--- a/source/blender/blenkernel/BKE_appdir.h
+++ b/source/blender/blenkernel/BKE_appdir.h
@@ -31,6 +31,8 @@ void BKE_appdir_exit(void);
/* note on naming: typical _get() suffix is omitted here,
* since its the main purpose of the API. */
const char *BKE_appdir_folder_default(void);
+const char *BKE_appdir_folder_home(void);
+bool BKE_appdir_folder_documents(char *dir);
bool BKE_appdir_folder_id_ex(const int folder_id,
const char *subfolder,
char *path,
diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h
new file mode 100644
index 00000000000..38cd5747343
--- /dev/null
+++ b/source/blender/blenkernel/BKE_asset.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef __BKE_ASSET_H__
+#define __BKE_ASSET_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BlendDataReader;
+struct BlendWriter;
+struct ID;
+struct PreviewImage;
+
+struct AssetMetaData *BKE_asset_metadata_create(void);
+void BKE_asset_metadata_free(struct AssetMetaData **asset_data);
+
+struct AssetTagEnsureResult {
+ struct AssetTag *tag;
+ /* Set to false if a tag of this name was already present. */
+ bool is_new;
+};
+
+struct AssetTag *BKE_asset_metadata_tag_add(struct AssetMetaData *asset_data, const char *name);
+struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *asset_data,
+ const char *name);
+void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag);
+
+struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
+ const struct ID *owner_id);
+
+void BKE_asset_metadata_write(struct BlendWriter *writer, struct AssetMetaData *asset_data);
+void BKE_asset_metadata_read(struct BlendDataReader *reader, struct AssetMetaData *asset_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_ASSET_H__ */
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index 55a841d8fd1..574d9904dc4 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -35,7 +35,6 @@ extern "C" {
struct CustomData;
struct CustomDataLayer;
struct ID;
-struct PointerRNA;
struct ReportList;
/* Attribute.domain */
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index c4a704ef385..22e14e44bec 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -26,8 +26,6 @@
#include "BLI_color.hh"
#include "BLI_float3.hh"
-struct Mesh;
-
namespace blender::bke {
using fn::CPPType;
@@ -266,11 +264,15 @@ template<typename T> class TypedWriteAttribute {
}
};
+using BooleanReadAttribute = TypedReadAttribute<bool>;
using FloatReadAttribute = TypedReadAttribute<float>;
using Float3ReadAttribute = TypedReadAttribute<float3>;
+using Int32ReadAttribute = TypedReadAttribute<int>;
using Color4fReadAttribute = TypedReadAttribute<Color4f>;
+using BooleanWriteAttribute = TypedWriteAttribute<bool>;
using FloatWriteAttribute = TypedWriteAttribute<float>;
using Float3WriteAttribute = TypedWriteAttribute<float3>;
+using Int32WriteAttribute = TypedWriteAttribute<int>;
using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index afb6112b954..1ed4d1183a1 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 6
+#define BLENDER_FILE_SUBVERSION 8
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h
index c9e6f0e7346..71a4d35767f 100644
--- a/source/blender/blenkernel/BKE_boids.h
+++ b/source/blender/blenkernel/BKE_boids.h
@@ -23,13 +23,16 @@
* \ingroup bke
*/
-#include "DNA_boid_types.h"
-#include "DNA_particle_types.h"
-
#ifdef __cplusplus
extern "C" {
#endif
+struct BoidSettings;
+struct BoidState;
+struct Object;
+struct ParticleData;
+struct ParticleSettings;
+struct ParticleSimulationData;
struct RNG;
typedef struct BoidBrainData {
@@ -50,13 +53,13 @@ typedef struct BoidBrainData {
void boids_precalc_rules(struct ParticleSettings *part, float cfra);
void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa);
void boid_body(BoidBrainData *bbd, struct ParticleData *pa);
-void boid_default_settings(BoidSettings *boids);
-BoidRule *boid_new_rule(int type);
-BoidState *boid_new_state(BoidSettings *boids);
-BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state);
-void boid_free_settings(BoidSettings *boids);
-BoidSettings *boid_copy_settings(const BoidSettings *boids);
-BoidState *boid_get_current_state(BoidSettings *boids);
+void boid_default_settings(struct BoidSettings *boids);
+struct BoidRule *boid_new_rule(int type);
+struct BoidState *boid_new_state(struct BoidSettings *boids);
+struct BoidState *boid_duplicate_state(struct BoidSettings *boids, struct BoidState *state);
+void boid_free_settings(struct BoidSettings *boids);
+struct BoidSettings *boid_copy_settings(const struct BoidSettings *boids);
+struct BoidState *boid_get_current_state(struct BoidSettings *boids);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h
index 3a5328a33e2..ff1bca896b1 100644
--- a/source/blender/blenkernel/BKE_collision.h
+++ b/source/blender/blenkernel/BKE_collision.h
@@ -22,21 +22,11 @@
* \ingroup bke
*/
-#include <float.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* types */
-#include "BKE_collision.h"
-#include "DNA_cloth_types.h"
-
-#include "BLI_kdopbvh.h"
-
#ifdef __cplusplus
extern "C" {
#endif
+struct BVHTree;
struct Collection;
struct CollisionModifierData;
struct Depsgraph;
@@ -113,11 +103,11 @@ typedef struct FaceCollPair {
// used in modifier.c from collision.c
/////////////////////////////////////////////////
-BVHTree *bvhtree_build_from_mvert(const struct MVert *mvert,
- const struct MVertTri *tri,
- int tri_num,
- float epsilon);
-void bvhtree_update_from_mvert(BVHTree *bvhtree,
+struct BVHTree *bvhtree_build_from_mvert(const struct MVert *mvert,
+ const struct MVertTri *tri,
+ int tri_num,
+ float epsilon);
+void bvhtree_update_from_mvert(struct BVHTree *bvhtree,
const struct MVert *mvert,
const struct MVert *mvert_moving,
const struct MVertTri *tri,
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index 7a14787c191..afad1e26159 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -23,6 +23,10 @@
* \ingroup bke
*/
+struct BlendDataReader;
+struct BlendExpander;
+struct BlendLibReader;
+struct BlendWriter;
struct Depsgraph;
struct ID;
struct ListBase;
@@ -31,10 +35,6 @@ struct Scene;
struct bConstraint;
struct bConstraintTarget;
struct bPoseChannel;
-struct BlendWriter;
-struct BlendDataReader;
-struct BlendLibReader;
-struct BlendExpander;
/* ---------------------------------------------------------------------------- */
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 5c534803781..94392dd78da 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -143,8 +143,9 @@ bContext *CTX_copy(const bContext *C);
/* Stored Context */
-bContextStore *CTX_store_add(ListBase *contexts, const char *name, PointerRNA *ptr);
+bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr);
bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context);
+bContextStore *CTX_store_get(bContext *C);
void CTX_store_set(bContext *C, bContextStore *store);
bContextStore *CTX_store_copy(bContextStore *store);
void CTX_store_free(bContextStore *store);
@@ -288,6 +289,9 @@ enum eContextObjectMode CTX_data_mode_enum(const bContext *C);
void CTX_data_main_set(bContext *C, struct Main *bmain);
void CTX_data_scene_set(bContext *C, struct Scene *scene);
+/* Only Outliner currently! */
+int CTX_data_selected_ids(const bContext *C, ListBase *list);
+
int CTX_data_selected_editable_objects(const bContext *C, ListBase *list);
int CTX_data_selected_editable_bases(const bContext *C, ListBase *list);
diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h
index 9ad4770c754..433c25084ad 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.h
+++ b/source/blender/blenkernel/BKE_cryptomatte.h
@@ -29,14 +29,21 @@
extern "C" {
#endif
-struct Object;
+struct Main;
struct Material;
+struct Object;
+uint32_t BKE_cryptomatte_hash(const char *name, int name_len);
uint32_t BKE_cryptomatte_object_hash(const struct Object *object);
uint32_t BKE_cryptomatte_material_hash(const struct Material *material);
uint32_t BKE_cryptomatte_asset_hash(const struct Object *object);
float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash);
+char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage);
+void BKE_cryptomatte_matte_id_to_entries(const struct Main *bmain,
+ struct NodeCryptomatte *node_storage,
+ const char *matte_id);
+
#ifdef __cplusplus
}
-#endif \ No newline at end of file
+#endif
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index dcb4a993da1..881b93fe709 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -22,7 +22,9 @@
* \ingroup bke
*/
-#include "DNA_scene_types.h"
+#include "BLI_sys_types.h"
+
+#include "DNA_listBase.h"
#ifdef __cplusplus
extern "C" {
@@ -318,8 +320,8 @@ void BKE_curve_deform_coords(const struct Object *ob_curve,
const short flag,
const short defaxis);
-void BKE_curve_deform_coords_with_editmesh(const Object *ob_curve,
- const Object *ob_target,
+void BKE_curve_deform_coords_with_editmesh(const struct Object *ob_curve,
+ const struct Object *ob_target,
float (*vert_coords)[3],
const int vert_coords_len,
const int defgrp_index,
diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h
index 0c84ad70845..2fb713a4299 100644
--- a/source/blender/blenkernel/BKE_editmesh.h
+++ b/source/blender/blenkernel/BKE_editmesh.h
@@ -35,9 +35,7 @@ struct BMLoop;
struct BMesh;
struct BoundBox;
struct Depsgraph;
-struct EditMeshData;
struct Mesh;
-struct MeshStatVis;
struct Object;
struct Scene;
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index 0585f67703c..3cba47afc46 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -22,8 +22,6 @@
* \ingroup bke
*/
-#include "DNA_modifier_types.h"
-
#include "BLI_utildefines.h"
#ifdef __cplusplus
@@ -147,7 +145,7 @@ float effector_falloff(struct EffectorCache *eff,
struct EffectorData *efd,
struct EffectedPoint *point,
struct EffectorWeights *weights);
-int closest_point_on_surface(SurfaceModifierData *surmd,
+int closest_point_on_surface(struct SurfaceModifierData *surmd,
const float co[3],
float surface_co[3],
float surface_nor[3],
diff --git a/source/blender/blenkernel/BKE_freestyle.h b/source/blender/blenkernel/BKE_freestyle.h
index 47f0b547d83..5e29665d728 100644
--- a/source/blender/blenkernel/BKE_freestyle.h
+++ b/source/blender/blenkernel/BKE_freestyle.h
@@ -23,8 +23,6 @@
* \ingroup bke
*/
-#include "DNA_scene_types.h"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -39,28 +37,31 @@ typedef struct FreestyleModuleSettings FreestyleModuleSettings;
typedef struct FreestyleSettings FreestyleSettings;
/* FreestyleConfig */
-void BKE_freestyle_config_init(FreestyleConfig *config);
-void BKE_freestyle_config_free(FreestyleConfig *config, const bool do_id_user);
-void BKE_freestyle_config_copy(FreestyleConfig *new_config,
- const FreestyleConfig *config,
+void BKE_freestyle_config_init(struct FreestyleConfig *config);
+void BKE_freestyle_config_free(struct FreestyleConfig *config, const bool do_id_user);
+void BKE_freestyle_config_copy(struct FreestyleConfig *new_config,
+ const struct FreestyleConfig *config,
const int flag);
/* FreestyleConfig.modules */
-FreestyleModuleConfig *BKE_freestyle_module_add(FreestyleConfig *config);
-bool BKE_freestyle_module_delete(FreestyleConfig *config, FreestyleModuleConfig *module_conf);
-bool BKE_freestyle_module_move(FreestyleConfig *config,
- FreestyleModuleConfig *module_conf,
+struct FreestyleModuleConfig *BKE_freestyle_module_add(struct FreestyleConfig *config);
+bool BKE_freestyle_module_delete(struct FreestyleConfig *config,
+ struct FreestyleModuleConfig *module_conf);
+bool BKE_freestyle_module_move(struct FreestyleConfig *config,
+ struct FreestyleModuleConfig *module_conf,
int direction);
/* FreestyleConfig.linesets */
-FreestyleLineSet *BKE_freestyle_lineset_add(struct Main *bmain,
- FreestyleConfig *config,
- const char *name);
-bool BKE_freestyle_lineset_delete(FreestyleConfig *config, FreestyleLineSet *lineset);
-FreestyleLineSet *BKE_freestyle_lineset_get_active(FreestyleConfig *config);
-short BKE_freestyle_lineset_get_active_index(FreestyleConfig *config);
-void BKE_freestyle_lineset_set_active_index(FreestyleConfig *config, short index);
-void BKE_freestyle_lineset_unique_name(FreestyleConfig *config, FreestyleLineSet *lineset);
+struct FreestyleLineSet *BKE_freestyle_lineset_add(struct Main *bmain,
+ struct FreestyleConfig *config,
+ const char *name);
+bool BKE_freestyle_lineset_delete(struct FreestyleConfig *config,
+ struct FreestyleLineSet *lineset);
+struct FreestyleLineSet *BKE_freestyle_lineset_get_active(struct FreestyleConfig *config);
+short BKE_freestyle_lineset_get_active_index(struct FreestyleConfig *config);
+void BKE_freestyle_lineset_set_active_index(struct FreestyleConfig *config, short index);
+void BKE_freestyle_lineset_unique_name(struct FreestyleConfig *config,
+ struct FreestyleLineSet *lineset);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index 026f4d39d51..37a3ed82bb8 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -24,18 +24,32 @@
extern "C" {
#endif
-struct Object;
+struct Collection;
struct GeometrySet;
+struct Object;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
+typedef enum InstancedDataType {
+ INSTANCE_DATA_TYPE_OBJECT = 0,
+ INSTANCE_DATA_TYPE_COLLECTION = 1,
+} InstancedDataType;
+
+typedef struct InstancedData {
+ InstancedDataType type;
+ union {
+ struct Object *object;
+ struct Collection *collection;
+ } data;
+} InstancedData;
+
int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
float (**r_positions)[3],
float (**r_rotations)[3],
float (**r_scales)[3],
- struct Object ***r_objects);
+ struct InstancedData **r_instanced_data);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 3398da9896b..e4232a84a00 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -32,9 +32,10 @@
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
+struct Collection;
struct Mesh;
-struct PointCloud;
struct Object;
+struct PointCloud;
/* Each geometry component has a specific type. The type determines what kind of data the component
* stores. Functions modifying a geometry will usually just modify a subset of the component types.
@@ -88,6 +89,9 @@ class GeometryComponent {
GeometryComponentType type() const;
+ /* Return true when any attribute with this name exists, including built in attributes. */
+ bool attribute_exists(const blender::StringRef attribute_name) const;
+
/* Returns true when the geometry component supports this attribute domain. */
virtual bool attribute_domain_supported(const AttributeDomain domain) const;
/* Returns true when the given data type is supported in the given domain. */
@@ -132,6 +136,11 @@ class GeometryComponent {
const AttributeDomain domain,
const CustomDataType data_type) const;
+ /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
+ * Returns null when the attribute does not exist. */
+ blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const AttributeDomain domain) const;
+
/* Get a read-only attribute for the given domain and data type.
* Returns a constant attribute based on the default value if the attribute does not exist.
* Never returns null. */
@@ -355,7 +364,7 @@ class InstancesComponent : public GeometryComponent {
blender::Vector<blender::float3> positions_;
blender::Vector<blender::float3> rotations_;
blender::Vector<blender::float3> scales_;
- blender::Vector<const Object *> objects_;
+ blender::Vector<InstancedData> instanced_data_;
public:
InstancesComponent();
@@ -363,12 +372,20 @@ class InstancesComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- void add_instance(const Object *object,
+ void add_instance(Object *object,
blender::float3 position,
blender::float3 rotation = {0, 0, 0},
blender::float3 scale = {1, 1, 1});
+ void add_instance(Collection *collection,
+ blender::float3 position,
+ blender::float3 rotation = {0, 0, 0},
+ blender::float3 scale = {1, 1, 1});
+ void add_instance(InstancedData data,
+ blender::float3 position,
+ blender::float3 rotation,
+ blender::float3 scale);
- blender::Span<const Object *> objects() const;
+ blender::Span<InstancedData> instanced_data() const;
blender::Span<blender::float3> positions() const;
blender::Span<blender::float3> rotations() const;
blender::Span<blender::float3> scales() const;
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 6dc8d1ef06e..df5711f5120 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -36,19 +36,17 @@ struct ListBase;
struct MDeformVert;
struct Main;
struct Material;
-struct MaterialGPencilStyle;
struct Object;
struct Scene;
struct SpaceImage;
struct ToolSettings;
struct ViewLayer;
struct bDeformGroup;
+struct bGPDcurve;
struct bGPDframe;
struct bGPDlayer;
struct bGPDlayer_Mask;
-struct bGPDspoint;
struct bGPDstroke;
-struct bGPDcurve;
struct bGPdata;
#define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE))
diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h
index 1821972469c..2d42bb36949 100644
--- a/source/blender/blenkernel/BKE_gpencil_curve.h
+++ b/source/blender/blenkernel/BKE_gpencil_curve.h
@@ -30,10 +30,10 @@ extern "C" {
struct Main;
struct Object;
struct Scene;
-struct bGPdata;
+struct bGPDcurve;
struct bGPDlayer;
struct bGPDstroke;
-struct bGPDcurve;
+struct bGPdata;
void BKE_gpencil_convert_curve(struct Main *bmain,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index b107a6e72af..1c86df73d3c 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -32,12 +32,11 @@ struct Depsgraph;
struct Main;
struct Object;
struct Scene;
+struct bGPDcurve;
struct bGPDframe;
-struct bGPDlayer;
struct bGPDspoint;
struct bGPDstroke;
struct bGPdata;
-struct bGPDcurve;
/* Object boundbox. */
bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]);
@@ -164,6 +163,11 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain,
const bool use_seams,
const bool use_faces);
+void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
+ struct bGPDstroke *gps,
+ const uint32_t target_number,
+ const bool select);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h
index ccf65a585ef..61ccf3d60f6 100644
--- a/source/blender/blenkernel/BKE_gpencil_modifier.h
+++ b/source/blender/blenkernel/BKE_gpencil_modifier.h
@@ -27,6 +27,9 @@ extern "C" {
#endif
struct ARegionType;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendWriter;
struct Depsgraph;
struct GpencilModifierData;
struct ID;
@@ -35,9 +38,6 @@ struct Main;
struct ModifierUpdateDepsgraphContext;
struct Object;
struct Scene;
-struct BlendWriter;
-struct BlendDataReader;
-struct BlendLibReader;
/* NOTE: bakeModifier() called from UI:
* needs to create new data-blocks, hence the need for this. */
struct bGPDframe;
diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h
index d6cf5eae323..28a6f837f61 100644
--- a/source/blender/blenkernel/BKE_icons.h
+++ b/source/blender/blenkernel/BKE_icons.h
@@ -23,17 +23,26 @@
* \ingroup bke
*
* Resizable Icons for Blender
+ *
+ * There is some thread safety for this API but it is rather weak. Registering or unregistering
+ * icons is thread safe, changing data of icons from multiple threads is not. Practically this
+ * should be fine since only the main thread modifies icons. Should that change, more locks or a
+ * different design need to be introduced.
*/
#ifdef __cplusplus
extern "C" {
#endif
+#include "BLI_compiler_attrs.h"
+
typedef void (*DrawInfoFreeFP)(void *drawinfo);
enum {
/** ID preview: obj is #ID. */
ICON_DATA_ID = 0,
+ /** Arbitrary Image buffer: obj is #ImBuf */
+ ICON_DATA_IMBUF,
/** Preview: obj is #PreviewImage */
ICON_DATA_PREVIEW,
/** 2D triangles: obj is #Icon_Geom */
@@ -44,6 +53,9 @@ enum {
ICON_DATA_GPLAYER,
};
+/**
+ * \note See comment at the top regarding thread safety.
+ */
struct Icon {
void *drawinfo;
/**
@@ -93,6 +105,9 @@ int BKE_icon_gplayer_color_ensure(struct bGPDlayer *gpl);
int BKE_icon_preview_ensure(struct ID *id, struct PreviewImage *preview);
+int BKE_icon_imbuf_create(struct ImBuf *ibuf) ATTR_WARN_UNUSED_RESULT;
+struct ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) ATTR_WARN_UNUSED_RESULT;
+
/* retrieve icon for id */
struct Icon *BKE_icon_get(const int icon_id);
@@ -129,6 +144,12 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
/* get the preview from any pointer */
struct PreviewImage **BKE_previewimg_id_get_p(const struct ID *id);
+struct PreviewImage *BKE_previewimg_id_get(const struct ID *id);
+
+bool BKE_previewimg_id_supports_jobs(const struct ID *id);
+
+/* Trigger deferred loading of a custom image file into the preview buffer. */
+void BKE_previewimg_id_custom_set(struct ID *id, const char *path);
/* free the preview image belonging to the id */
void BKE_previewimg_id_free(struct ID *id);
@@ -146,6 +167,11 @@ struct PreviewImage *BKE_previewimg_id_ensure(struct ID *id);
void BKE_previewimg_ensure(struct PreviewImage *prv, const int size);
+struct ImBuf *BKE_previewimg_to_imbuf(struct PreviewImage *prv, const int size);
+
+void BKE_previewimg_finish(struct PreviewImage *prv, const int size);
+bool BKE_previewimg_is_finished(const struct PreviewImage *prv, const int size);
+
struct PreviewImage *BKE_previewimg_cached_get(const char *name);
struct PreviewImage *BKE_previewimg_cached_ensure(const char *name);
@@ -156,13 +182,14 @@ struct PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name,
bool force_update);
void BKE_previewimg_cached_release(const char *name);
-void BKE_previewimg_cached_release_pointer(struct PreviewImage *prv);
+
+void BKE_previewimg_deferred_release(struct PreviewImage *prv);
void BKE_previewimg_blend_write(struct BlendWriter *writer, const struct PreviewImage *prv);
void BKE_previewimg_blend_read(struct BlendDataReader *reader, struct PreviewImage *prv);
int BKE_icon_geom_ensure(struct Icon_Geom *geom);
-struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len);
+struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len);
struct Icon_Geom *BKE_icon_geom_from_file(const char *filename);
struct ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom,
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index 9c250240e5e..bcf35bf1197 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -20,8 +20,6 @@
* \ingroup bke
*/
-#include "DNA_ID.h"
-
#include "BLI_compiler_attrs.h"
#ifdef __cplusplus
@@ -57,9 +55,9 @@ typedef union IDPropertyTemplate {
/* ----------- Property Array Type ---------- */
-IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
+struct IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+struct IDProperty *IDP_CopyIDPArray(const struct IDProperty *array,
+ const int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/* shallow copies item */
void IDP_SetIndexArray(struct IDProperty *prop, int index, struct IDProperty *item) ATTR_NONNULL();
@@ -74,7 +72,9 @@ void IDP_ResizeArray(struct IDProperty *prop, int newlen);
void IDP_FreeArray(struct IDProperty *prop);
/* ---------- String Type ------------ */
-IDProperty *IDP_NewString(const char *st, const char *name, int maxlen) ATTR_WARN_UNUSED_RESULT
+struct IDProperty *IDP_NewString(const char *st,
+ const char *name,
+ int maxlen) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(2 /* 'name 'arg */); /* maxlen excludes '\0' */
void IDP_AssignString(struct IDProperty *prop, const char *st, int maxlen)
ATTR_NONNULL(); /* maxlen excludes '\0' */
@@ -84,9 +84,9 @@ void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL();
/*-------- ID Type -------*/
-typedef void (*IDPWalkFunc)(void *userData, IDProperty *idp);
+typedef void (*IDPWalkFunc)(void *userData, struct IDProperty *idp);
-void IDP_AssignID(IDProperty *prop, ID *id, const int flag);
+void IDP_AssignID(struct IDProperty *prop, struct ID *id, const int flag);
/*-------- Group Functions -------*/
@@ -100,10 +100,10 @@ void IDP_ReplaceInGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_
void IDP_ReplaceInGroup_ex(struct IDProperty *group,
struct IDProperty *prop,
struct IDProperty *prop_exist);
-void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite)
+void IDP_MergeGroup(struct IDProperty *dest, const struct IDProperty *src, const bool do_overwrite)
ATTR_NONNULL();
-void IDP_MergeGroup_ex(IDProperty *dest,
- const IDProperty *src,
+void IDP_MergeGroup_ex(struct IDProperty *dest,
+ const struct IDProperty *src,
const bool do_overwrite,
const int flag) ATTR_NONNULL();
bool IDP_AddToGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL();
@@ -113,11 +113,13 @@ bool IDP_InsertToGroup(struct IDProperty *group,
void IDP_RemoveFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL();
void IDP_FreeFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL();
-IDProperty *IDP_GetPropertyFromGroup(const struct IDProperty *prop,
- const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop,
- const char *name,
- const char type) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+struct IDProperty *IDP_GetPropertyFromGroup(const struct IDProperty *prop,
+ const char *name) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop,
+ const char *name,
+ const char type) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
/*-------- Main Functions --------*/
struct IDProperty *IDP_GetProperties(struct ID *id,
@@ -127,10 +129,10 @@ struct IDProperty *IDP_CopyProperty(const struct IDProperty *prop) ATTR_WARN_UNU
ATTR_NONNULL();
struct IDProperty *IDP_CopyProperty_ex(const struct IDProperty *prop,
const int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-void IDP_CopyPropertyContent(IDProperty *dst, IDProperty *src) ATTR_NONNULL();
+void IDP_CopyPropertyContent(struct IDProperty *dst, struct IDProperty *src) ATTR_NONNULL();
-bool IDP_EqualsProperties_ex(IDProperty *prop1,
- IDProperty *prop2,
+bool IDP_EqualsProperties_ex(struct IDProperty *prop1,
+ struct IDProperty *prop2,
const bool is_strict) ATTR_WARN_UNUSED_RESULT;
bool IDP_EqualsProperties(struct IDProperty *prop1,
@@ -142,12 +144,12 @@ struct IDProperty *IDP_New(const char type,
void IDP_FreePropertyContent_ex(struct IDProperty *prop, const bool do_id_user);
void IDP_FreePropertyContent(struct IDProperty *prop);
-void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user);
+void IDP_FreeProperty_ex(struct IDProperty *prop, const bool do_id_user);
void IDP_FreeProperty(struct IDProperty *prop);
-void IDP_ClearProperty(IDProperty *prop);
+void IDP_ClearProperty(struct IDProperty *prop);
-void IDP_Reset(IDProperty *prop, const IDProperty *reference);
+void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference);
#define IDP_Int(prop) ((prop)->data.val)
#define IDP_Array(prop) ((prop)->data.pointer)
@@ -155,29 +157,29 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference);
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
# define IDP_Float(prop) \
_Generic((prop), \
- IDProperty *: (*(float *)&(prop)->data.val), \
- const IDProperty *: (*(const float *)&(prop)->data.val))
+ struct IDProperty *: (*(float *)&(prop)->data.val), \
+ const struct IDProperty *: (*(const float *)&(prop)->data.val))
# define IDP_Double(prop) \
_Generic((prop), \
- IDProperty *: (*(double *)&(prop)->data.val), \
- const IDProperty *: (*(const double *)&(prop)->data.val))
+ struct IDProperty *: (*(double *)&(prop)->data.val), \
+ const struct IDProperty *: (*(const double *)&(prop)->data.val))
# define IDP_String(prop) \
_Generic((prop), \
- IDProperty *: ((char *) (prop)->data.pointer), \
- const IDProperty *: ((const char *) (prop)->data.pointer))
+ struct IDProperty *: ((char *) (prop)->data.pointer), \
+ const struct IDProperty *: ((const char *) (prop)->data.pointer))
# define IDP_IDPArray(prop) \
_Generic((prop), \
- IDProperty *: ((IDProperty *) (prop)->data.pointer), \
- const IDProperty *: ((const IDProperty *) (prop)->data.pointer))
+ struct IDProperty *: ((struct IDProperty *) (prop)->data.pointer), \
+ const struct IDProperty *: ((const struct IDProperty *) (prop)->data.pointer))
# define IDP_Id(prop) \
_Generic((prop), \
- IDProperty *: ((ID *) (prop)->data.pointer), \
- const IDProperty *: ((const ID *) (prop)->data.pointer))
+ struct IDProperty *: ((ID *) (prop)->data.pointer), \
+ const struct IDProperty *: ((const ID *) (prop)->data.pointer))
#else
# define IDP_Float(prop) (*(float *)&(prop)->data.val)
# define IDP_Double(prop) (*(double *)&(prop)->data.val)
# define IDP_String(prop) ((char *)(prop)->data.pointer)
-# define IDP_IDPArray(prop) ((IDProperty *)(prop)->data.pointer)
+# define IDP_IDPArray(prop) ((struct IDProperty *)(prop)->data.pointer)
# define IDP_Id(prop) ((ID *)(prop)->data.pointer)
#endif
@@ -185,7 +187,7 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference);
* Call a callback for each idproperty in the hierarchy under given root one (included).
*
*/
-typedef void (*IDPForeachPropertyCallback)(IDProperty *id_property, void *user_data);
+typedef void (*IDPForeachPropertyCallback)(struct IDProperty *id_property, void *user_data);
void IDP_foreach_property(struct IDProperty *id_property_root,
const int type_filter,
@@ -194,18 +196,18 @@ void IDP_foreach_property(struct IDProperty *id_property_root,
/* Format IDProperty as strings */
char *IDP_reprN(const struct IDProperty *prop, uint *r_len);
-void IDP_repr_fn(const IDProperty *prop,
+void IDP_repr_fn(const struct IDProperty *prop,
void (*str_append_fn)(void *user_data, const char *str, uint str_len),
void *user_data);
void IDP_print(const struct IDProperty *prop);
void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop);
void IDP_BlendReadData_impl(struct BlendDataReader *reader,
- IDProperty **prop,
+ struct IDProperty **prop,
const char *caller_func_id);
#define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__)
-void IDP_BlendReadLib(struct BlendLibReader *reader, IDProperty *prop);
-void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop);
+void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop);
+void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_ipo.h b/source/blender/blenkernel/BKE_ipo.h
index 40b9f738bfd..f4871c83caf 100644
--- a/source/blender/blenkernel/BKE_ipo.h
+++ b/source/blender/blenkernel/BKE_ipo.h
@@ -26,7 +26,6 @@
extern "C" {
#endif
-struct Ipo;
struct Main;
void do_versions_ipos_to_animato(struct Main *main);
diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h
index f4c1a6fdcb4..02fa8b306d3 100644
--- a/source/blender/blenkernel/BKE_lattice.h
+++ b/source/blender/blenkernel/BKE_lattice.h
@@ -38,7 +38,6 @@ struct Main;
struct Mesh;
struct Object;
struct Scene;
-struct bGPDstroke;
void BKE_lattice_resize(struct Lattice *lt, int u, int v, int w, struct Object *ltOb);
struct Lattice *BKE_lattice_add(struct Main *bmain, const char *name);
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index e5fab35891c..240d6cb18ec 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -23,7 +23,6 @@
#include "BKE_collection.h"
#include "DNA_listBase.h"
-#include "DNA_scene_types.h"
#ifdef __cplusplus
extern "C" {
@@ -56,9 +55,9 @@ typedef enum eViewLayerCopyMethod {
struct ViewLayer *BKE_view_layer_default_view(const struct Scene *scene);
struct ViewLayer *BKE_view_layer_default_render(const struct Scene *scene);
struct ViewLayer *BKE_view_layer_find(const struct Scene *scene, const char *layer_name);
-struct ViewLayer *BKE_view_layer_add(Scene *scene,
+struct ViewLayer *BKE_view_layer_add(struct Scene *scene,
const char *name,
- ViewLayer *view_layer_source,
+ struct ViewLayer *view_layer_source,
const int type);
/* DEPRECATED */
@@ -393,10 +392,11 @@ struct ObjectsInModeParams {
void *filter_userdata;
};
-Base **BKE_view_layer_array_from_bases_in_mode_params(struct ViewLayer *view_layer,
- const struct View3D *v3d,
- uint *r_len,
- const struct ObjectsInModeParams *params);
+struct Base **BKE_view_layer_array_from_bases_in_mode_params(
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len,
+ const struct ObjectsInModeParams *params);
struct Object **BKE_view_layer_array_from_objects_in_mode_params(
struct ViewLayer *view_layer,
@@ -452,7 +452,8 @@ void BKE_view_layer_verify_aov(struct RenderEngine *engine,
struct Scene *scene,
struct ViewLayer *view_layer);
bool BKE_view_layer_has_valid_aov(struct ViewLayer *view_layer);
-ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV *view_layer_aov);
+struct ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene,
+ struct ViewLayerAOV *view_layer_aov);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index e54e2fb4b87..b0971278dc7 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -51,7 +51,6 @@
extern "C" {
#endif
-struct BlendDataReader;
struct BlendWriter;
struct GHash;
struct ID;
@@ -298,6 +297,8 @@ void BKE_id_tag_clear_atomic(struct ID *id, int tag);
bool BKE_id_is_in_global_main(struct ID *id);
+bool BKE_id_can_be_asset(const struct ID *id);
+
void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb);
void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index bf6b5cbccef..13edabd4cb7 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -72,6 +72,7 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain,
void BKE_lib_override_library_override_group_tag(struct Main *bmain,
struct ID *id_root,
const uint tag,
+ const uint missing_tag,
const bool do_create_main_relashionships);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index fb14e340d8b..a8d75213d39 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -38,8 +38,6 @@
extern "C" {
#endif
-struct wmWindowManager;
-
/* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */
/* Also IDRemap->flag. */
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 807f13efe14..2d8dc852d7c 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -26,7 +26,6 @@
#include "BLI_utildefines.h"
struct BLI_Stack;
-struct BMEditMesh;
struct BMesh;
struct BMeshCreateParams;
struct BMeshFromMeshParams;
@@ -678,7 +677,7 @@ void BKE_mesh_calc_edges_loose(struct Mesh *mesh);
void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, const bool select_new_edges);
void BKE_mesh_calc_edges_tessface(struct Mesh *mesh);
-/* In DerivedMesh.c */
+/* In DerivedMesh.cc */
void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval,
const CustomData_MeshMasks *cd_mask_finalize);
diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h
new file mode 100644
index 00000000000..2d5c85d4129
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_fair.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Mesh Fairing. */
+/* Creates a smooth as possible geometry patch in a defined area. Different values of depth allow
+ * to minimize changes in the vertex positions or tangency in the affected area. */
+
+typedef enum eMeshFairingDepth {
+ MESH_FAIRING_DEPTH_POSITION = 1,
+ MESH_FAIRING_DEPTH_TANGENCY = 2,
+} eMeshFairingDepth;
+
+/* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when
+ * the vertex should be modified by fairing. */
+void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth);
+
+/* This function can optionally use the MVert coordinates of deform_mverts to read and write the
+ * fairing result. When NULL, the function will use mesh->mverts directly. */
+void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h
index 87b55c581a2..67c87e96aff 100644
--- a/source/blender/blenkernel/BKE_mesh_runtime.h
+++ b/source/blender/blenkernel/BKE_mesh_runtime.h
@@ -57,9 +57,9 @@ void BKE_mesh_runtime_verttri_from_looptri(struct MVertTri *r_verttri,
const struct MLoopTri *looptri,
int looptri_num);
-/* NOTE: the functions below are defined in DerivedMesh.c, and are intended to be moved
+/* NOTE: the functions below are defined in DerivedMesh.cc, and are intended to be moved
* to a more suitable location when that file is removed.
- * They should also be renamed to use conventions from BKE, not old DerivedMesh.c.
+ * They should also be renamed to use conventions from BKE, not old DerivedMesh.cc.
* For now keep the names similar to avoid confusion. */
struct Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 73e33124b43..685a8ed98e2 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -35,6 +35,7 @@ struct BlendWriter;
struct CustomData_MeshMasks;
struct DepsNodeHandle;
struct Depsgraph;
+struct GeometrySet;
struct ID;
struct ListBase;
struct Main;
@@ -43,7 +44,6 @@ struct ModifierData;
struct Object;
struct Scene;
struct bArmature;
-struct GeometrySet;
typedef enum {
/* Should not be used, only for None modifier type */
@@ -244,12 +244,20 @@ typedef struct ModifierTypeInfo {
struct Mesh *(*modifyMesh)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Mesh *mesh);
+
struct Hair *(*modifyHair)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Hair *hair);
- void (*modifyPointCloud)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct GeometrySet *geometry_set);
+
+ /**
+ * The modifier has to change the geometry set in-place. The geometry set can contain zero or
+ * more geometry components. This callback can be used by modifiers that don't work on any
+ * specific type of geometry (e.g. mesh).
+ */
+ void (*modifyGeometrySet)(struct ModifierData *md,
+ const struct ModifierEvalContext *ctx,
+ struct GeometrySet *geometry_set);
+
struct Volume *(*modifyVolume)(struct ModifierData *md,
const struct ModifierEvalContext *ctx,
struct Volume *volume);
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index 5a668532033..fce25abba7f 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -23,14 +23,13 @@
* \ingroup bke
*/
+#include "BKE_subsurf.h"
#include "BLI_compiler_compat.h"
#ifdef __cplusplus
extern "C" {
#endif
-enum MultiresModifiedFlags;
-
struct Depsgraph;
struct DerivedMesh;
struct MDisps;
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index a8a94958772..5f69fc397e8 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -451,7 +451,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type);
bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup);
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree);
void ntreeUpdateAllNew(struct Main *main);
-void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup);
+void ntreeUpdateAllUsers(struct Main *main, struct ID *id);
void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes);
@@ -1347,9 +1347,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINT_INSTANCE 1005
#define GEO_NODE_SUBDIVISION_SURFACE 1006
#define GEO_NODE_OBJECT_INFO 1007
-#define GEO_NODE_RANDOM_ATTRIBUTE 1008
+#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008
#define GEO_NODE_ATTRIBUTE_MATH 1009
#define GEO_NODE_JOIN_GEOMETRY 1010
+#define GEO_NODE_ATTRIBUTE_FILL 1011
+#define GEO_NODE_ATTRIBUTE_MIX 1012
+#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013
/** \} */
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 479bc56b2dc..aaed2649ad9 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -25,6 +25,7 @@
#include "BLI_bitmap.h"
#include "BLI_utildefines.h"
+#include "DNA_brush_enums.h"
#include "DNA_object_enums.h"
#ifdef __cplusplus
@@ -33,6 +34,9 @@ extern "C" {
struct BMFace;
struct BMesh;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendWriter;
struct Brush;
struct CurveMapping;
struct Depsgraph;
@@ -54,7 +58,6 @@ struct Paint;
struct PaintCurve;
struct Palette;
struct PaletteColor;
-struct ReportList;
struct Scene;
struct StrokeCache;
struct SubdivCCG;
@@ -66,11 +69,6 @@ struct ViewLayer;
struct bContext;
struct bToolRef;
struct tPaletteColorHSV;
-struct BlendWriter;
-struct BlendDataReader;
-struct BlendLibReader;
-
-enum eOverlayFlags;
extern const char PAINT_CURSOR_SCULPT[3];
extern const char PAINT_CURSOR_VERTEX_PAINT[3];
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 73815325594..3913ede9049 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -29,11 +29,8 @@
#include "BLI_buffer.h"
#include "BLI_utildefines.h"
-#include "DNA_object_types.h"
#include "DNA_particle_types.h"
-#include "BKE_customdata.h"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -45,9 +42,9 @@ struct ParticleSystemModifierData;
struct BVHTreeRay;
struct BVHTreeRayHit;
-struct BlendWriter;
struct BlendDataReader;
struct BlendLibReader;
+struct BlendWriter;
struct CustomData_MeshMasks;
struct Depsgraph;
struct EdgeHash;
@@ -302,7 +299,7 @@ int psys_get_tot_child(struct Scene *scene,
struct ParticleSystem *psys_get_current(struct Object *ob);
/* for rna */
short psys_get_current_num(struct Object *ob);
-void psys_set_current_num(Object *ob, int index);
+void psys_set_current_num(struct Object *ob, int index);
/* UNUSED */
// struct Object *psys_find_object(struct Scene *scene, struct ParticleSystem *psys);
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index fd600a41796..0fa44067b16 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -48,7 +48,6 @@ struct PBVH;
struct PBVHNode;
struct SubdivCCG;
struct TaskParallelSettings;
-struct TaskParallelTLS;
typedef struct PBVH PBVH;
typedef struct PBVHNode PBVHNode;
diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
index 42a853886d7..bbee09c7bf4 100644
--- a/source/blender/blenkernel/BKE_persistent_data_handle.hh
+++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh
@@ -27,6 +27,7 @@
#include "DNA_ID.h"
+struct Collection;
struct Object;
namespace blender::bke {
@@ -82,6 +83,11 @@ class PersistentObjectHandle : public PersistentIDHandle {
using PersistentIDHandle::PersistentIDHandle;
};
+class PersistentCollectionHandle : public PersistentIDHandle {
+ friend PersistentDataHandleMap;
+ using PersistentIDHandle::PersistentIDHandle;
+};
+
class PersistentDataHandleMap {
private:
Map<int32_t, ID *> id_by_handle_;
@@ -107,6 +113,12 @@ class PersistentDataHandleMap {
return PersistentObjectHandle(handle);
}
+ PersistentCollectionHandle lookup(Collection *collection) const
+ {
+ const int handle = handle_by_id_.lookup_default((ID *)collection, -1);
+ return PersistentCollectionHandle(handle);
+ }
+
ID *lookup(const PersistentIDHandle &handle) const
{
ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr);
@@ -124,6 +136,18 @@ class PersistentDataHandleMap {
}
return (Object *)id;
}
+
+ Collection *lookup(const PersistentCollectionHandle &handle) const
+ {
+ ID *id = this->lookup((const PersistentIDHandle &)handle);
+ if (id == nullptr) {
+ return nullptr;
+ }
+ if (GS(id->name) != ID_GR) {
+ return nullptr;
+ }
+ return (Collection *)id;
+ }
};
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index f2edebededc..170eb4ba662 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -23,12 +23,10 @@
* \ingroup bke
*/
-#include "DNA_ID.h"
-#include "DNA_boid_types.h"
-#include "DNA_dynamicpaint_types.h"
-#include "DNA_object_force_types.h"
-#include "DNA_pointcache_types.h"
-#include <stdio.h> /* for FILE */
+#include "DNA_boid_types.h" /* for #BoidData */
+#include "DNA_pointcache_types.h" /* for #BPHYS_TOT_DATA */
+
+#include <stdio.h> /* for #FILE */
#ifdef __cplusplus
extern "C" {
@@ -79,7 +77,10 @@ extern "C" {
#define PTCACHE_READ_OLD 3
/* Structs */
+struct BlendDataReader;
+struct BlendWriter;
struct ClothModifierData;
+struct DynamicPaintSurface;
struct FluidModifierData;
struct ListBase;
struct Main;
@@ -91,8 +92,6 @@ struct RigidBodyWorld;
struct Scene;
struct SoftBody;
struct ViewLayer;
-struct BlendWriter;
-struct BlendDataReader;
/* temp structure for read/write */
typedef struct PTCacheData {
diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h
new file mode 100644
index 00000000000..04a41d425bb
--- /dev/null
+++ b/source/blender/blenkernel/BKE_preferences.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "BLI_compiler_attrs.h"
+
+struct UserDef;
+struct bUserAssetLibrary;
+
+void BKE_preferences_asset_library_free(struct bUserAssetLibrary *library) ATTR_NONNULL();
+
+struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef,
+ const char *name,
+ const char *path) ATTR_NONNULL(1);
+void BKE_preferences_asset_library_name_set(struct UserDef *userdef,
+ struct bUserAssetLibrary *library,
+ const char *name) ATTR_NONNULL();
+
+void BKE_preferences_asset_library_remove(struct UserDef *userdef,
+ struct bUserAssetLibrary *library) ATTR_NONNULL();
+
+struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(
+ const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(
+ const struct UserDef *userdef, const char *name) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+int BKE_preferences_asset_library_get_index(const struct UserDef *userdef,
+ const struct bUserAssetLibrary *library)
+ ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+
+void BKE_preferences_asset_library_default_add(struct UserDef *userdef) ATTR_NONNULL();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 473a684eaba..7b5df98d148 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -51,7 +51,6 @@ struct View3D;
struct View3DShading;
struct WorkSpace;
struct bContext;
-struct bContextDataResult;
struct bScreen;
struct uiLayout;
struct uiList;
diff --git a/source/blender/blenkernel/BKE_sequencer_offscreen.h b/source/blender/blenkernel/BKE_sequencer_offscreen.h
index f5f6067b06e..25a78fcfbad 100644
--- a/source/blender/blenkernel/BKE_sequencer_offscreen.h
+++ b/source/blender/blenkernel/BKE_sequencer_offscreen.h
@@ -23,26 +23,23 @@
* \ingroup bke
*/
-#include "DNA_object_enums.h"
-
-#include "DNA_view3d_types.h"
-
-#include "IMB_imbuf_types.h"
-
#ifdef __cplusplus
extern "C" {
#endif
struct GPUOffScreen;
+enum eDrawType;
+enum eImBufFlags;
+
typedef struct ImBuf *(*SequencerDrawView)(struct Depsgraph *depsgraph,
struct Scene *scene,
struct View3DShading *shading_override,
- eDrawType drawtype,
+ enum eDrawType drawtype,
struct Object *camera,
int width,
int height,
- eImBufFlags flag,
+ enum eImBufFlags flag,
eV3DOffscreenDrawFlag draw_flags,
int alpha_mode,
const char *viewname,
diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h
index 7c2d363be5c..23bd62c70bc 100644
--- a/source/blender/blenkernel/BKE_shader_fx.h
+++ b/source/blender/blenkernel/BKE_shader_fx.h
@@ -19,7 +19,6 @@
* \ingroup bke
*/
-#include "BKE_customdata.h"
#include "BLI_compiler_attrs.h"
#include "DNA_shader_fx_types.h" /* needed for all enum typdefs */
@@ -28,14 +27,14 @@ extern "C" {
#endif
struct ARegionType;
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendWriter;
struct ID;
struct ListBase;
struct ModifierUpdateDepsgraphContext;
struct Object;
struct ShaderFxData;
-struct BlendWriter;
-struct BlendDataReader;
-struct BlendLibReader;
#define SHADER_FX_ACTIVE(_fx, _is_render) \
((((_fx)->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \
diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h
index 23735990079..37372036130 100644
--- a/source/blender/blenkernel/BKE_simulation.h
+++ b/source/blender/blenkernel/BKE_simulation.h
@@ -16,8 +16,6 @@
#pragma once
-#include "DNA_simulation_types.h"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -25,6 +23,7 @@ extern "C" {
struct Depsgraph;
struct Main;
struct Scene;
+struct Simulation;
void *BKE_simulation_add(struct Main *bmain, const char *name);
diff --git a/source/blender/blenkernel/BKE_speaker.h b/source/blender/blenkernel/BKE_speaker.h
index e288c9f3eb4..9defa887d3c 100644
--- a/source/blender/blenkernel/BKE_speaker.h
+++ b/source/blender/blenkernel/BKE_speaker.h
@@ -26,7 +26,6 @@ extern "C" {
#endif
struct Main;
-struct Speaker;
void *BKE_speaker_add(struct Main *bmain, const char *name);
diff --git a/source/blender/blenkernel/BKE_studiolight.h b/source/blender/blenkernel/BKE_studiolight.h
index 614523be123..70b8743bcd2 100644
--- a/source/blender/blenkernel/BKE_studiolight.h
+++ b/source/blender/blenkernel/BKE_studiolight.h
@@ -29,7 +29,7 @@
#include "BLI_path_util.h"
-#include "DNA_userdef_types.h"
+#include "DNA_userdef_types.h" /* for #SolidLight */
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/blenkernel/BKE_text_suggestions.h b/source/blender/blenkernel/BKE_text_suggestions.h
index f54e45b6c2f..7561e1d1d08 100644
--- a/source/blender/blenkernel/BKE_text_suggestions.h
+++ b/source/blender/blenkernel/BKE_text_suggestions.h
@@ -22,8 +22,6 @@
* \ingroup bke
*/
-#include "DNA_text_types.h"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -62,9 +60,9 @@ typedef struct SuggList {
void free_texttools(void);
/* Used to identify which Text object the current tools should appear against */
-void texttool_text_set_active(Text *text);
+void texttool_text_set_active(struct Text *text);
void texttool_text_clear(void);
-short texttool_text_is_active(Text *text);
+short texttool_text_is_active(struct Text *text);
/* Suggestions */
void texttool_suggest_add(const char *name, char type);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index c962f0a6a8c..ae0a180311c 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -36,12 +36,13 @@ set(INC
../makesrna
../modifiers
../nodes
+ ../render
../sequencer
../shader_fx
../simulation
- ../render
../../../intern/ghost
../../../intern/glew-mx
+ ../../../intern/eigen
../../../intern/guardedalloc
../../../intern/iksolver/extern
../../../intern/atomic
@@ -66,7 +67,7 @@ set(SRC
intern/CCGSubSurf.c
intern/CCGSubSurf_legacy.c
intern/CCGSubSurf_util.c
- intern/DerivedMesh.c
+ intern/DerivedMesh.cc
intern/action.c
intern/addon.c
intern/anim_data.c
@@ -77,6 +78,7 @@ set(SRC
intern/armature.c
intern/armature_deform.c
intern/armature_update.c
+ intern/asset.c
intern/attribute.c
intern/attribute_access.cc
intern/autoexec.c
@@ -101,7 +103,7 @@ set(SRC
intern/constraint.c
intern/context.c
intern/crazyspace.c
- intern/cryptomatte.c
+ intern/cryptomatte.cc
intern/curve.c
intern/curve_bevel.c
intern/curve_decimate.c
@@ -132,7 +134,7 @@ set(SRC
intern/gpencil_geom.c
intern/gpencil_modifier.c
intern/hair.c
- intern/icons.c
+ intern/icons.cc
intern/icons_rasterize.c
intern/idprop.c
intern/idprop_utils.c
@@ -169,6 +171,7 @@ set(SRC
intern/mesh.c
intern/mesh_convert.c
intern/mesh_evaluate.c
+ intern/mesh_fair.cc
intern/mesh_iterators.c
intern/mesh_mapping.c
intern/mesh_merge.c
@@ -214,6 +217,7 @@ set(SRC
intern/pbvh_bmesh.c
intern/pointcache.c
intern/pointcloud.cc
+ intern/preferences.c
intern/report.c
intern/rigidbody.c
intern/scene.c
@@ -271,6 +275,7 @@ set(SRC
BKE_attribute.h
BKE_attribute_access.hh
BKE_autoexec.h
+ BKE_asset.h
BKE_blender.h
BKE_blender_copybuffer.h
BKE_blender_undo.h
@@ -350,6 +355,7 @@ set(SRC
BKE_mball_tessellate.h
BKE_mesh.h
BKE_mesh_iterators.h
+ BKE_mesh_fair.h
BKE_mesh_mapping.h
BKE_mesh_mirror.h
BKE_mesh_remap.h
@@ -374,6 +380,7 @@ set(SRC
BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
+ BKE_preferences.h
BKE_report.h
BKE_rigidbody.h
BKE_scene.h
@@ -726,8 +733,8 @@ if(WITH_GTESTS)
intern/armature_test.cc
intern/fcurve_test.cc
intern/lattice_deform_test.cc
- intern/tracking_test.cc
intern/layer_test.cc
+ intern/tracking_test.cc
)
set(TEST_INC
../editors/include
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.cc
index eeff04788f9..b8219dcf7ac 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -21,8 +21,8 @@
* \ingroup bke
*/
-#include <limits.h>
-#include <string.h>
+#include <climits>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -38,16 +38,19 @@
#include "BLI_array.h"
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
+#include "BLI_float2.hh"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_DerivedMesh.h"
#include "BKE_bvhutils.h"
#include "BKE_colorband.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@@ -81,7 +84,7 @@
#ifdef USE_MODIFIER_VALIDATE
# define ASSERT_IS_VALID_MESH(mesh) \
- (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true)))
+ (BLI_assert((mesh == nullptr) || (BKE_mesh_is_valid(mesh) == true)))
#else
# define ASSERT_IS_VALID_MESH(mesh)
#endif
@@ -96,10 +99,11 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
static MVert *dm_getVertArray(DerivedMesh *dm)
{
- MVert *mvert = CustomData_get_layer(&dm->vertData, CD_MVERT);
+ MVert *mvert = (MVert *)CustomData_get_layer(&dm->vertData, CD_MVERT);
if (!mvert) {
- mvert = CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, dm->getNumVerts(dm));
+ mvert = (MVert *)CustomData_add_layer(
+ &dm->vertData, CD_MVERT, CD_CALLOC, nullptr, dm->getNumVerts(dm));
CustomData_set_layer_flag(&dm->vertData, CD_MVERT, CD_FLAG_TEMPORARY);
dm->copyVertArray(dm, mvert);
}
@@ -109,10 +113,11 @@ static MVert *dm_getVertArray(DerivedMesh *dm)
static MEdge *dm_getEdgeArray(DerivedMesh *dm)
{
- MEdge *medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE);
+ MEdge *medge = (MEdge *)CustomData_get_layer(&dm->edgeData, CD_MEDGE);
if (!medge) {
- medge = CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_CALLOC, NULL, dm->getNumEdges(dm));
+ medge = (MEdge *)CustomData_add_layer(
+ &dm->edgeData, CD_MEDGE, CD_CALLOC, nullptr, dm->getNumEdges(dm));
CustomData_set_layer_flag(&dm->edgeData, CD_MEDGE, CD_FLAG_TEMPORARY);
dm->copyEdgeArray(dm, medge);
}
@@ -122,7 +127,7 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm)
static MFace *dm_getTessFaceArray(DerivedMesh *dm)
{
- MFace *mface = CustomData_get_layer(&dm->faceData, CD_MFACE);
+ MFace *mface = (MFace *)CustomData_get_layer(&dm->faceData, CD_MFACE);
if (!mface) {
int numTessFaces = dm->getNumTessFaces(dm);
@@ -132,10 +137,11 @@ static MFace *dm_getTessFaceArray(DerivedMesh *dm)
* this layer is needed with non-zero size, but currently CD stuff does not check
* for requested layer size on creation and just returns layer which was previously
* added (sergey) */
- return NULL;
+ return nullptr;
}
- mface = CustomData_add_layer(&dm->faceData, CD_MFACE, CD_CALLOC, NULL, numTessFaces);
+ mface = (MFace *)CustomData_add_layer(
+ &dm->faceData, CD_MFACE, CD_CALLOC, nullptr, numTessFaces);
CustomData_set_layer_flag(&dm->faceData, CD_MFACE, CD_FLAG_TEMPORARY);
dm->copyTessFaceArray(dm, mface);
}
@@ -145,10 +151,11 @@ static MFace *dm_getTessFaceArray(DerivedMesh *dm)
static MLoop *dm_getLoopArray(DerivedMesh *dm)
{
- MLoop *mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
+ MLoop *mloop = (MLoop *)CustomData_get_layer(&dm->loopData, CD_MLOOP);
if (!mloop) {
- mloop = CustomData_add_layer(&dm->loopData, CD_MLOOP, CD_CALLOC, NULL, dm->getNumLoops(dm));
+ mloop = (MLoop *)CustomData_add_layer(
+ &dm->loopData, CD_MLOOP, CD_CALLOC, nullptr, dm->getNumLoops(dm));
CustomData_set_layer_flag(&dm->loopData, CD_MLOOP, CD_FLAG_TEMPORARY);
dm->copyLoopArray(dm, mloop);
}
@@ -158,10 +165,11 @@ static MLoop *dm_getLoopArray(DerivedMesh *dm)
static MPoly *dm_getPolyArray(DerivedMesh *dm)
{
- MPoly *mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);
+ MPoly *mpoly = (MPoly *)CustomData_get_layer(&dm->polyData, CD_MPOLY);
if (!mpoly) {
- mpoly = CustomData_add_layer(&dm->polyData, CD_MPOLY, CD_CALLOC, NULL, dm->getNumPolys(dm));
+ mpoly = (MPoly *)CustomData_add_layer(
+ &dm->polyData, CD_MPOLY, CD_CALLOC, nullptr, dm->getNumPolys(dm));
CustomData_set_layer_flag(&dm->polyData, CD_MPOLY, CD_FLAG_TEMPORARY);
dm->copyPolyArray(dm, mpoly);
}
@@ -171,7 +179,8 @@ static MPoly *dm_getPolyArray(DerivedMesh *dm)
static MVert *dm_dupVertArray(DerivedMesh *dm)
{
- MVert *tmp = MEM_malloc_arrayN(dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp");
+ MVert *tmp = (MVert *)MEM_malloc_arrayN(
+ dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp");
if (tmp) {
dm->copyVertArray(dm, tmp);
@@ -182,7 +191,8 @@ static MVert *dm_dupVertArray(DerivedMesh *dm)
static MEdge *dm_dupEdgeArray(DerivedMesh *dm)
{
- MEdge *tmp = MEM_malloc_arrayN(dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp");
+ MEdge *tmp = (MEdge *)MEM_malloc_arrayN(
+ dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp");
if (tmp) {
dm->copyEdgeArray(dm, tmp);
@@ -193,7 +203,8 @@ static MEdge *dm_dupEdgeArray(DerivedMesh *dm)
static MFace *dm_dupFaceArray(DerivedMesh *dm)
{
- MFace *tmp = MEM_malloc_arrayN(dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp");
+ MFace *tmp = (MFace *)MEM_malloc_arrayN(
+ dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp");
if (tmp) {
dm->copyTessFaceArray(dm, tmp);
@@ -204,7 +215,8 @@ static MFace *dm_dupFaceArray(DerivedMesh *dm)
static MLoop *dm_dupLoopArray(DerivedMesh *dm)
{
- MLoop *tmp = MEM_malloc_arrayN(dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp");
+ MLoop *tmp = (MLoop *)MEM_malloc_arrayN(
+ dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp");
if (tmp) {
dm->copyLoopArray(dm, tmp);
@@ -215,7 +227,8 @@ static MLoop *dm_dupLoopArray(DerivedMesh *dm)
static MPoly *dm_dupPolyArray(DerivedMesh *dm)
{
- MPoly *tmp = MEM_malloc_arrayN(dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp");
+ MPoly *tmp = (MPoly *)MEM_malloc_arrayN(
+ dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp");
if (tmp) {
dm->copyPolyArray(dm, tmp);
@@ -239,14 +252,14 @@ static const MLoopTri *dm_getLoopTriArray(DerivedMesh *dm)
looptri = dm->looptris.array;
BLI_rw_mutex_unlock(&loops_cache_lock);
- if (looptri != NULL) {
+ if (looptri != nullptr) {
BLI_assert(dm->getNumLoopTri(dm) == dm->looptris.num);
}
else {
BLI_rw_mutex_lock(&loops_cache_lock, THREAD_LOCK_WRITE);
- /* We need to ensure array is still NULL inside mutex-protected code,
+ /* We need to ensure array is still nullptr inside mutex-protected code,
* some other thread might have already recomputed those looptris. */
- if (dm->looptris.array == NULL) {
+ if (dm->looptris.array == nullptr) {
dm->recalcLoopTri(dm);
}
looptri = dm->looptris.array;
@@ -343,7 +356,7 @@ void DM_init(DerivedMesh *dm,
DM_init_funcs(dm);
dm->needsFree = 1;
- dm->dirty = 0;
+ dm->dirty = (DMDirtyFlag)0;
/* don't use CustomData_reset(...); because we dont want to touch customdata */
copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1);
@@ -385,7 +398,7 @@ void DM_from_template_ex(DerivedMesh *dm,
DM_init_funcs(dm);
dm->needsFree = 1;
- dm->dirty = 0;
+ dm->dirty = (DMDirtyFlag)0;
}
void DM_from_template(DerivedMesh *dm,
DerivedMesh *source,
@@ -482,7 +495,7 @@ void DM_ensure_looptri_data(DerivedMesh *dm)
const unsigned int totloop = dm->numLoopData;
const int looptris_num = poly_to_tri_count(totpoly, totloop);
- BLI_assert(dm->looptris.array_wip == NULL);
+ BLI_assert(dm->looptris.array_wip == nullptr);
SWAP(MLoopTri *, dm->looptris.array, dm->looptris.array_wip);
@@ -494,8 +507,8 @@ void DM_ensure_looptri_data(DerivedMesh *dm)
}
if (totpoly) {
- if (dm->looptris.array_wip == NULL) {
- dm->looptris.array_wip = MEM_malloc_arrayN(
+ if (dm->looptris.array_wip == nullptr) {
+ dm->looptris.array_wip = (MLoopTri *)MEM_malloc_arrayN(
looptris_num, sizeof(*dm->looptris.array_wip), __func__);
dm->looptris.num_alloc = looptris_num;
}
@@ -656,7 +669,7 @@ void DM_interp_vert_data(DerivedMesh *source,
int dest_index)
{
CustomData_interp(
- &source->vertData, &dest->vertData, src_indices, weights, NULL, count, dest_index);
+ &source->vertData, &dest->vertData, src_indices, weights, nullptr, count, dest_index);
}
static float (*get_editbmesh_orco_verts(BMEditMesh *em))[3]
@@ -669,7 +682,7 @@ static float (*get_editbmesh_orco_verts(BMEditMesh *em))[3]
/* these may not really be the orco's, but it's only for preview.
* could be solver better once, but isn't simple */
- orco = MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "BMEditMesh Orco");
+ orco = (float(*)[3])MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "BMEditMesh Orco");
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
copy_v3_v3(orco[i], eve->co);
@@ -702,14 +715,14 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free)
clmd->sim_parms->shapekey_rest);
if (kb && kb->data) {
- return kb->data;
+ return (float(*)[3])kb->data;
}
}
- return NULL;
+ return nullptr;
}
- return NULL;
+ return nullptr;
}
static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer)
@@ -719,7 +732,7 @@ static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer)
int free;
if (em) {
- mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, me);
+ mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, me);
}
else {
mesh = BKE_mesh_copy_for_eval(me, true);
@@ -748,10 +761,10 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc
free = 1;
if (mesh_orco->totvert == totvert) {
- orco = BKE_mesh_vert_coords_alloc(mesh_orco, NULL);
+ orco = BKE_mesh_vert_coords_alloc(mesh_orco, nullptr);
}
else {
- orco = BKE_mesh_vert_coords_alloc(mesh, NULL);
+ orco = BKE_mesh_vert_coords_alloc(mesh, nullptr);
}
}
else {
@@ -762,14 +775,14 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc
if (orco) {
if (layer == CD_ORCO) {
- BKE_mesh_orco_verts_transform(ob->data, orco, totvert, 0);
+ BKE_mesh_orco_verts_transform((Mesh *)ob->data, orco, totvert, 0);
}
- if (!(layerorco = CustomData_get_layer(&mesh->vdata, layer))) {
- CustomData_add_layer(&mesh->vdata, layer, CD_CALLOC, NULL, mesh->totvert);
+ if (!(layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer))) {
+ CustomData_add_layer(&mesh->vdata, layer, CD_CALLOC, nullptr, mesh->totvert);
BKE_mesh_update_customdata_pointers(mesh, false);
- layerorco = CustomData_get_layer(&mesh->vdata, layer);
+ layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer);
}
memcpy(layerorco, orco, sizeof(float[3]) * totvert);
@@ -797,10 +810,10 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input,
* (BKE_mesh_calc_normals_split() assumes that if that data exists, it is always valid). */
if (do_poly_normals) {
if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) {
- float(*polynors)[3] = CustomData_add_layer(
- &mesh_final->pdata, CD_NORMAL, CD_CALLOC, NULL, mesh_final->totpoly);
+ float(*polynors)[3] = (float(*)[3])CustomData_add_layer(
+ &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly);
BKE_mesh_calc_normals_poly(mesh_final->mvert,
- NULL,
+ nullptr,
mesh_final->totvert,
mesh_final->mloop,
mesh_final->mpoly,
@@ -869,6 +882,56 @@ void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval,
BLI_assert(me_eval->runtime.wrapper_type_finalize == 0);
}
+/**
+ * Modifies the given mesh and geometry set. The geometry set is expect to have NO mesh component.
+ * After this function ends, the geometry set will still have NO mesh component. Instead, an input
+ * mesh is passed separately and is returned separately.
+ *
+ * The purpose of the geometry set is to store all non-mesh geometry components that are generated
+ * by modifiers.
+ */
+static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
+ const ModifierEvalContext &mectx,
+ Object *ob,
+ Mesh *input_mesh,
+ GeometrySet &geometry_set)
+{
+ Mesh *mesh_output = nullptr;
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
+ if (mti->modifyGeometrySet == nullptr) {
+ mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh);
+ }
+ else {
+ /* For performance reasons, this should be called by the modifier and/or nodes themselves at
+ * some point. */
+ BKE_mesh_wrapper_ensure_mdata(input_mesh);
+
+ /* Adds a new mesh component to the geometry set based on the #input_mesh. */
+ BLI_assert(!geometry_set.has<MeshComponent>());
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(input_mesh, GeometryOwnershipType::Editable);
+ mesh_component.copy_vertex_group_names_from_object(*ob);
+
+ /* Let the modifier change the geometry set. */
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
+
+ /* Release the mesh from the geometry set again. */
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_output = mesh_component.release();
+ geometry_set.remove<MeshComponent>();
+ }
+
+ /* Return an empty mesh instead of null. */
+ if (mesh_output == nullptr) {
+ mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
+ BKE_mesh_copy_settings(mesh_output, input_mesh);
+ }
+ }
+
+ return mesh_output;
+}
+
static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
Scene *scene,
Object *ob,
@@ -880,45 +943,50 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
const bool allow_shared_mesh,
/* return args */
Mesh **r_deform,
- Mesh **r_final)
+ Mesh **r_final,
+ GeometrySet **r_geometry_set)
{
/* Input and final mesh. Final mesh is only created the moment the first
* constructive modifier is executed, or a deform modifier needs normals
* or certain data layers. */
- Mesh *mesh_input = ob->data;
- Mesh *mesh_final = NULL;
- Mesh *mesh_deform = NULL;
+ Mesh *mesh_input = (Mesh *)ob->data;
+ Mesh *mesh_final = nullptr;
+ Mesh *mesh_deform = nullptr;
+ /* This geometry set contains the non-mesh data that might be generated by modifiers. */
+ GeometrySet geometry_set_final;
BLI_assert((mesh_input->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0);
/* Deformed vertex locations array. Deform only modifier need this type of
* float array rather than MVert*. Tracked along with mesh_final as an
* optimization to avoid copying coordinates back and forth if there are
* multiple sequential deform only modifiers. */
- float(*deformed_verts)[3] = NULL;
+ float(*deformed_verts)[3] = nullptr;
int num_deformed_verts = mesh_input->totvert;
bool isPrevDeform = false;
/* Mesh with constructive modifiers but no deformation applied. Tracked
* along with final mesh if undeformed / orco coordinates are requested
* for texturing. */
- Mesh *mesh_orco = NULL;
- Mesh *mesh_orco_cloth = NULL;
+ Mesh *mesh_orco = nullptr;
+ Mesh *mesh_orco_cloth = nullptr;
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
/* Sculpt can skip certain modifiers. */
- const bool has_multires = BKE_sculpt_multires_active(scene, ob) != NULL;
+ const bool has_multires = BKE_sculpt_multires_active(scene, ob) != nullptr;
bool multires_applied = false;
const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render;
const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render;
/* Modifier evaluation contexts for different types of modifiers. */
- ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : 0;
- ModifierApplyFlag apply_cache = use_cache ? MOD_APPLY_USECACHE : 0;
- const ModifierEvalContext mectx = {depsgraph, ob, apply_render | apply_cache};
- const ModifierEvalContext mectx_orco = {depsgraph, ob, apply_render | MOD_APPLY_ORCO};
+ ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0;
+ ModifierApplyFlag apply_cache = use_cache ? MOD_APPLY_USECACHE : (ModifierApplyFlag)0;
+ const ModifierEvalContext mectx = {
+ depsgraph, ob, (ModifierApplyFlag)(apply_render | apply_cache)};
+ const ModifierEvalContext mectx_orco = {
+ depsgraph, ob, (ModifierApplyFlag)(apply_render | MOD_APPLY_ORCO)};
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
@@ -930,10 +998,10 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
* even if the resulting data is not used in a material. Only in object mode.
* TODO: this is broken, not drawn by the drawn manager. */
const bool do_mod_mcol = (ob->mode == OB_MODE_OBJECT);
- ModifierData *previewmd = NULL;
+ ModifierData *previewmd = nullptr;
CustomData_MeshMasks previewmask = {0};
if (do_mod_mcol) {
- /* Find the last active modifier generating a preview, or NULL if none. */
+ /* Find the last active modifier generating a preview, or nullptr if none. */
/* XXX Currently, DPaint modifier just ignores this.
* Needs a stupid hack...
* The whole "modifier preview" thing has to be (re?)designed, anyway! */
@@ -957,7 +1025,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Apply all leading deform modifiers. */
if (useDeform) {
for (; md; md = md->next, md_datamask = md_datamask->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -972,7 +1040,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
deformed_verts = BKE_mesh_vert_coords_alloc(mesh_input, &num_deformed_verts);
}
else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) {
- if (mesh_final == NULL) {
+ if (mesh_final == nullptr) {
mesh_final = BKE_mesh_copy_for_eval(mesh_input, true);
ASSERT_IS_VALID_MESH(mesh_final);
}
@@ -989,7 +1057,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* grab modifiers until index i */
if ((index != -1) && (BLI_findindex(&ob->modifiers, md) >= index)) {
- md = NULL;
+ md = nullptr;
break;
}
}
@@ -1009,7 +1077,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Apply all remaining constructive and deforming modifiers. */
bool have_non_onlydeform_modifiers_appled = false;
for (; md; md = md->next, md_datamask = md_datamask->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -1069,7 +1137,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
CustomData_MeshMasks mask = {0};
mti->requiredDataMask(ob, md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
- add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_ORCO);
+ add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_ORCO);
}
}
@@ -1092,7 +1160,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* if this is not the last modifier in the stack then recalculate the normals
* to avoid giving bogus normals to the next modifier see: T23673. */
else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) {
- if (mesh_final == NULL) {
+ if (mesh_final == nullptr) {
mesh_final = BKE_mesh_copy_for_eval(mesh_input, true);
ASSERT_IS_VALID_MESH(mesh_final);
}
@@ -1103,7 +1171,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
else {
bool check_for_needs_mapping = false;
/* apply vertex coordinates or build a Mesh as necessary */
- if (mesh_final != NULL) {
+ if (mesh_final != nullptr) {
if (have_non_onlydeform_modifiers_appled == false) {
/* If we only deformed, we won't have initialized #CD_ORIGINDEX.
* as this is the only part of the function that initializes mapping. */
@@ -1137,20 +1205,23 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
((nextmask.vmask | nextmask.emask | nextmask.pmask) & CD_MASK_ORIGINDEX)) {
/* calc */
CustomData_add_layer(
- &mesh_final->vdata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totvert);
+ &mesh_final->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totvert);
CustomData_add_layer(
- &mesh_final->edata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totedge);
+ &mesh_final->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totedge);
CustomData_add_layer(
- &mesh_final->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totpoly);
+ &mesh_final->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totpoly);
/* Not worth parallelizing this,
* gives less than 0.1% overall speedup in best of best cases... */
- range_vn_i(
- CustomData_get_layer(&mesh_final->vdata, CD_ORIGINDEX), mesh_final->totvert, 0);
- range_vn_i(
- CustomData_get_layer(&mesh_final->edata, CD_ORIGINDEX), mesh_final->totedge, 0);
- range_vn_i(
- CustomData_get_layer(&mesh_final->pdata, CD_ORIGINDEX), mesh_final->totpoly, 0);
+ range_vn_i((int *)CustomData_get_layer(&mesh_final->vdata, CD_ORIGINDEX),
+ mesh_final->totvert,
+ 0);
+ range_vn_i((int *)CustomData_get_layer(&mesh_final->edata, CD_ORIGINDEX),
+ mesh_final->totedge,
+ 0);
+ range_vn_i((int *)CustomData_get_layer(&mesh_final->pdata, CD_ORIGINDEX),
+ mesh_final->totpoly,
+ 0);
}
}
@@ -1168,49 +1239,50 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* add cloth rest shape key if needed */
if (mask.vmask & CD_MASK_CLOTH_ORCO) {
- add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_CLOTH_ORCO);
+ add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_CLOTH_ORCO);
}
/* add an origspace layer if needed */
if ((md_datamask->mask.lmask) & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
- &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, NULL, mesh_final->totloop);
+ &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop);
mesh_init_origspace(mesh_final);
}
}
- Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final);
+ Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(
+ md, mectx, ob, mesh_final, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
/* if the modifier returned a new mesh, release the old one */
if (mesh_final != mesh_next) {
BLI_assert(mesh_final != mesh_input);
- BKE_id_free(NULL, mesh_final);
+ BKE_id_free(nullptr, mesh_final);
}
mesh_final = mesh_next;
if (deformed_verts) {
MEM_freeN(deformed_verts);
- deformed_verts = NULL;
+ deformed_verts = nullptr;
}
}
/* create an orco mesh in parallel */
if (nextmask.vmask & CD_MASK_ORCO) {
if (!mesh_orco) {
- mesh_orco = create_orco_mesh(ob, mesh_input, NULL, CD_ORCO);
+ mesh_orco = create_orco_mesh(ob, mesh_input, nullptr, CD_ORCO);
}
nextmask.vmask &= ~CD_MASK_ORCO;
- CustomData_MeshMasks temp_cddata_masks = {
- .vmask = CD_MASK_ORIGINDEX,
- .emask = CD_MASK_ORIGINDEX,
- .fmask = CD_MASK_ORIGINDEX,
- .pmask = CD_MASK_ORIGINDEX,
- };
- if (mti->requiredDataMask != NULL) {
+ CustomData_MeshMasks temp_cddata_masks = {0};
+ temp_cddata_masks.vmask = CD_MASK_ORIGINDEX;
+ temp_cddata_masks.emask = CD_MASK_ORIGINDEX;
+ temp_cddata_masks.fmask = CD_MASK_ORIGINDEX;
+ temp_cddata_masks.pmask = CD_MASK_ORIGINDEX;
+
+ if (mti->requiredDataMask != nullptr) {
mti->requiredDataMask(ob, md, &temp_cddata_masks);
}
CustomData_MeshMasks_update(&temp_cddata_masks, &nextmask);
@@ -1223,7 +1295,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* if the modifier returned a new mesh, release the old one */
if (mesh_orco != mesh_next) {
BLI_assert(mesh_orco != mesh_input);
- BKE_id_free(NULL, mesh_orco);
+ BKE_id_free(nullptr, mesh_orco);
}
mesh_orco = mesh_next;
@@ -1233,7 +1305,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* create cloth orco mesh in parallel */
if (nextmask.vmask & CD_MASK_CLOTH_ORCO) {
if (!mesh_orco_cloth) {
- mesh_orco_cloth = create_orco_mesh(ob, mesh_input, NULL, CD_CLOTH_ORCO);
+ mesh_orco_cloth = create_orco_mesh(ob, mesh_input, nullptr, CD_CLOTH_ORCO);
}
nextmask.vmask &= ~CD_MASK_CLOTH_ORCO;
@@ -1249,7 +1321,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* if the modifier returned a new mesh, release the old one */
if (mesh_orco_cloth != mesh_next) {
BLI_assert(mesh_orco != mesh_input);
- BKE_id_free(NULL, mesh_orco_cloth);
+ BKE_id_free(nullptr, mesh_orco_cloth);
}
mesh_orco_cloth = mesh_next;
@@ -1277,7 +1349,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
- BLI_linklist_free((LinkNode *)datamasks, NULL);
+ BLI_linklist_free((LinkNode *)datamasks, nullptr);
for (md = firstmd; md; md = md->next) {
BKE_modifier_free_temporary_data(md);
@@ -1286,11 +1358,11 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Yay, we are done. If we have a Mesh and deformed vertices,
* we need to apply these back onto the Mesh. If we have no
* Mesh then we need to build one. */
- if (mesh_final == NULL) {
+ if (mesh_final == nullptr) {
/* Note: this check on cdmask is a bit dodgy, it handles the issue at stake here (see T68211),
* but other cases might require similar handling?
* Could be a good idea to define a proper CustomData_MeshMask for that then. */
- if (deformed_verts == NULL && allow_shared_mesh &&
+ if (deformed_verts == nullptr && allow_shared_mesh &&
(final_datamask.lmask & CD_MASK_NORMAL) == 0 &&
(final_datamask.pmask & CD_MASK_NORMAL) == 0) {
mesh_final = mesh_input;
@@ -1302,7 +1374,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
if (deformed_verts) {
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
MEM_freeN(deformed_verts);
- deformed_verts = NULL;
+ deformed_verts = nullptr;
}
/* Denotes whether the object which the modifier stack came from owns the mesh or whether the
@@ -1314,19 +1386,19 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* No need in ORCO layer if the mesh was not deformed or modified: undeformed mesh in this case
* matches input mesh. */
if (is_own_mesh) {
- add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_ORCO);
+ add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_ORCO);
}
if (mesh_deform) {
- add_orco_mesh(ob, NULL, mesh_deform, NULL, CD_ORCO);
+ add_orco_mesh(ob, nullptr, mesh_deform, nullptr, CD_ORCO);
}
}
if (mesh_orco) {
- BKE_id_free(NULL, mesh_orco);
+ BKE_id_free(nullptr, mesh_orco);
}
if (mesh_orco_cloth) {
- BKE_id_free(NULL, mesh_orco_cloth);
+ BKE_id_free(nullptr, mesh_orco_cloth);
}
/* Compute normals. */
@@ -1335,16 +1407,16 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
}
else {
Mesh_Runtime *runtime = &mesh_input->runtime;
- if (runtime->mesh_eval == NULL) {
- BLI_assert(runtime->eval_mutex != NULL);
- BLI_mutex_lock(runtime->eval_mutex);
- if (runtime->mesh_eval == NULL) {
+ if (runtime->mesh_eval == nullptr) {
+ BLI_assert(runtime->eval_mutex != nullptr);
+ BLI_mutex_lock((ThreadMutex *)runtime->eval_mutex);
+ if (runtime->mesh_eval == nullptr) {
mesh_final = BKE_mesh_copy_for_eval(mesh_input, true);
mesh_calc_modifier_final_normals(mesh_input, &final_datamask, sculpt_dyntopo, mesh_final);
mesh_calc_finalize(mesh_input, mesh_final);
runtime->mesh_eval = mesh_final;
}
- BLI_mutex_unlock(runtime->eval_mutex);
+ BLI_mutex_unlock((ThreadMutex *)runtime->eval_mutex);
}
mesh_final = runtime->mesh_eval;
}
@@ -1358,6 +1430,9 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
if (r_deform) {
*r_deform = mesh_deform;
}
+ if (r_geometry_set) {
+ *r_geometry_set = new GeometrySet(std::move(geometry_set_final));
+ }
}
float (*editbmesh_vert_coords_alloc(BMEditMesh *em, int *r_vert_len))[3]
@@ -1369,7 +1444,7 @@ float (*editbmesh_vert_coords_alloc(BMEditMesh *em, int *r_vert_len))[3]
*r_vert_len = em->bm->totvert;
- cos = MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "vertexcos");
+ cos = (float(*)[3])MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "vertexcos");
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
copy_v3_v3(cos[i], eve->co);
@@ -1383,7 +1458,7 @@ bool editbmesh_modifier_is_enabled(Scene *scene,
ModifierData *md,
bool has_prev_mesh)
{
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode;
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
@@ -1417,10 +1492,10 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
* (BKE_mesh_calc_normals_split() assumes that if that data exists, it is always valid). */
if (do_poly_normals) {
if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) {
- float(*polynors)[3] = CustomData_add_layer(
- &mesh_final->pdata, CD_NORMAL, CD_CALLOC, NULL, mesh_final->totpoly);
+ float(*polynors)[3] = (float(*)[3])CustomData_add_layer(
+ &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly);
BKE_mesh_calc_normals_poly(mesh_final->mvert,
- NULL,
+ nullptr,
mesh_final->totvert,
mesh_final->mloop,
mesh_final->mpoly,
@@ -1442,7 +1517,7 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
* check if the derived meshes are DM_TYPE_EDITBMESH before calling, this isn't essential
* but quiets annoying error messages since tessfaces wont be created. */
if (final_datamask->fmask & CD_MASK_MFACE) {
- if (mesh_final->edit_mesh == NULL) {
+ if (mesh_final->edit_mesh == nullptr) {
BKE_mesh_tessface_ensure(mesh_final);
}
}
@@ -1466,35 +1541,39 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
const CustomData_MeshMasks *dataMask,
/* return args */
Mesh **r_cage,
- Mesh **r_final)
+ Mesh **r_final,
+ GeometrySet **r_geometry_set)
{
/* Input and final mesh. Final mesh is only created the moment the first
* constructive modifier is executed, or a deform modifier needs normals
* or certain data layers. */
- Mesh *mesh_input = ob->data;
- Mesh *mesh_final = NULL;
- Mesh *mesh_cage = NULL;
+ Mesh *mesh_input = (Mesh *)ob->data;
+ Mesh *mesh_final = nullptr;
+ Mesh *mesh_cage = nullptr;
+ /* This geometry set contains the non-mesh data that might be generated by modifiers. */
+ GeometrySet geometry_set_final;
/* Deformed vertex locations array. Deform only modifier need this type of
* float array rather than MVert*. Tracked along with mesh_final as an
* optimization to avoid copying coordinates back and forth if there are
* multiple sequential deform only modifiers. */
- float(*deformed_verts)[3] = NULL;
+ float(*deformed_verts)[3] = nullptr;
int num_deformed_verts = 0;
bool isPrevDeform = false;
/* Mesh with constructive modifiers but no deformation applied. Tracked
* along with final mesh if undeformed / orco coordinates are requested
* for texturing. */
- Mesh *mesh_orco = NULL;
+ Mesh *mesh_orco = nullptr;
/* Modifier evaluation modes. */
const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode;
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
/* Modifier evaluation contexts for different types of modifiers. */
- ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : 0;
- const ModifierEvalContext mectx = {depsgraph, ob, MOD_APPLY_USECACHE | apply_render};
+ ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0;
+ const ModifierEvalContext mectx = {
+ depsgraph, ob, (ModifierApplyFlag)(MOD_APPLY_USECACHE | apply_render)};
const ModifierEvalContext mectx_orco = {depsgraph, ob, MOD_APPLY_ORCO};
/* Get effective list of modifiers to execute. Some effects like shape keys
@@ -1508,24 +1587,24 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
* subdividing them is expensive. */
CustomData_MeshMasks final_datamask = *dataMask;
CDMaskLink *datamasks = BKE_modifier_calc_data_masks(
- scene, ob, md, &final_datamask, required_mode, NULL, NULL);
+ scene, ob, md, &final_datamask, required_mode, nullptr, nullptr);
CDMaskLink *md_datamask = datamasks;
CustomData_MeshMasks append_mask = CD_MASK_BAREMESH;
/* Evaluate modifiers up to certain index to get the mesh cage. */
- int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1);
+ int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true);
if (r_cage && cageIndex == -1) {
mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords(
- em_input, &final_datamask, NULL, mesh_input);
+ em_input, &final_datamask, nullptr, mesh_input);
}
/* Clear errors before evaluation. */
BKE_modifiers_clear_errors(ob);
for (int i = 0; md; i++, md = md->next, md_datamask = md_datamask->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
- if (!editbmesh_modifier_is_enabled(scene, ob, md, mesh_final != NULL)) {
+ if (!editbmesh_modifier_is_enabled(scene, ob, md, mesh_final != nullptr)) {
continue;
}
@@ -1555,11 +1634,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
}
else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) {
- if (mesh_final == NULL) {
- mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, NULL, mesh_input);
+ if (mesh_final == nullptr) {
+ mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input);
ASSERT_IS_VALID_MESH(mesh_final);
}
- BLI_assert(deformed_verts != NULL);
+ BLI_assert(deformed_verts != nullptr);
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
}
@@ -1577,7 +1656,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (deformed_verts) {
Mesh *mesh_tmp = BKE_mesh_copy_for_eval(mesh_final, false);
if (mesh_final != mesh_cage) {
- BKE_id_free(NULL, mesh_final);
+ BKE_id_free(nullptr, mesh_final);
}
mesh_final = mesh_tmp;
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
@@ -1589,8 +1668,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
else {
mesh_final = BKE_mesh_wrapper_from_editmesh_with_coords(
- em_input, NULL, deformed_verts, mesh_input);
- deformed_verts = NULL;
+ em_input, nullptr, deformed_verts, mesh_input);
+ deformed_verts = nullptr;
}
/* create an orco derivedmesh in parallel */
@@ -1612,7 +1691,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (mesh_next) {
/* if the modifier returned a new dm, release the old one */
if (mesh_orco && mesh_orco != mesh_next) {
- BKE_id_free(NULL, mesh_orco);
+ BKE_id_free(nullptr, mesh_orco);
}
mesh_orco = mesh_next;
}
@@ -1632,23 +1711,24 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (mask.lmask & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) {
CustomData_add_layer(
- &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, NULL, mesh_final->totloop);
+ &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop);
mesh_init_origspace(mesh_final);
}
}
- Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final);
+ Mesh *mesh_next = modifier_modify_mesh_and_geometry_set(
+ md, mectx, ob, mesh_final, geometry_set_final);
ASSERT_IS_VALID_MESH(mesh_next);
if (mesh_next) {
if (mesh_final && mesh_final != mesh_next) {
- BKE_id_free(NULL, mesh_final);
+ BKE_id_free(nullptr, mesh_final);
}
mesh_final = mesh_next;
if (deformed_verts) {
MEM_freeN(deformed_verts);
- deformed_verts = NULL;
+ deformed_verts = nullptr;
}
}
mesh_final->runtime.deformed_only = false;
@@ -1668,12 +1748,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) {
BKE_mesh_runtime_reset_edit_data(me_orig);
}
- me_orig->runtime.edit_data->vertexCos = MEM_dupallocN(deformed_verts);
+ me_orig->runtime.edit_data->vertexCos = (float(*)[3])MEM_dupallocN(deformed_verts);
}
mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords(
em_input,
&final_datamask,
- deformed_verts ? MEM_dupallocN(deformed_verts) : NULL,
+ deformed_verts ? (float(*)[3])MEM_dupallocN(deformed_verts) : nullptr,
mesh_input);
}
}
@@ -1681,7 +1761,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
isPrevDeform = (mti->type == eModifierTypeType_OnlyDeform);
}
- BLI_linklist_free((LinkNode *)datamasks, NULL);
+ BLI_linklist_free((LinkNode *)datamasks, nullptr);
/* Yay, we are done. If we have a DerivedMesh and deformed vertices need
* to apply these back onto the DerivedMesh. If we have no DerivedMesh
@@ -1690,7 +1770,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (deformed_verts) {
Mesh *mesh_tmp = BKE_mesh_copy_for_eval(mesh_final, false);
if (mesh_final != mesh_cage) {
- BKE_id_free(NULL, mesh_final);
+ BKE_id_free(nullptr, mesh_final);
}
mesh_final = mesh_tmp;
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
@@ -1704,7 +1784,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* this is just a copy of the editmesh, no need to calc normals */
mesh_final = BKE_mesh_wrapper_from_editmesh_with_coords(
em_input, &final_datamask, deformed_verts, mesh_input);
- deformed_verts = NULL;
+ deformed_verts = nullptr;
}
if (deformed_verts) {
@@ -1720,7 +1800,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
if (mesh_orco) {
- BKE_id_free(NULL, mesh_orco);
+ BKE_id_free(nullptr, mesh_orco);
}
/* Ensure normals calculation below is correct. */
@@ -1739,6 +1819,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (r_cage) {
*r_cage = mesh_cage;
}
+ if (r_geometry_set) {
+ *r_geometry_set = new GeometrySet(std::move(geometry_set_final));
+ }
}
static void mesh_build_extra_data(struct Depsgraph *depsgraph, Object *ob, Mesh *mesh_eval)
@@ -1784,7 +1867,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
}
#endif
- Mesh *mesh_eval = NULL, *mesh_deform_eval = NULL;
+ Mesh *mesh_eval = nullptr, *mesh_deform_eval = nullptr;
+ GeometrySet *geometry_set_eval = nullptr;
mesh_calc_modifiers(depsgraph,
scene,
ob,
@@ -1795,7 +1879,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
true,
true,
&mesh_deform_eval,
- &mesh_eval);
+ &mesh_eval,
+ &geometry_set_eval);
/* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result
* is not guaranteed to be owned by object.
@@ -1803,10 +1888,16 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
* Check ownership now, since later on we can not go to a mesh owned by someone else via
* object's runtime: this could cause access freed data on depsgraph destruction (mesh who owns
* the final result might be freed prior to object). */
- Mesh *mesh = ob->data;
+ Mesh *mesh = (Mesh *)ob->data;
const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime.mesh_eval);
BKE_object_eval_assign_data(ob, &mesh_eval->id, is_mesh_eval_owned);
+ /* Add the final mesh as read-only non-owning component to the geometry set. */
+ BLI_assert(!geometry_set_eval->has<MeshComponent>());
+ MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly);
+ ob->runtime.geometry_set_eval = geometry_set_eval;
+
ob->runtime.mesh_deform_eval = mesh_deform_eval;
ob->runtime.last_data_mask = *dataMask;
ob->runtime.last_need_mapping = need_mapping;
@@ -1816,7 +1907,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph,
/* Make sure that drivers can target shapekey properties.
* Note that this causes a potential inconsistency, as the shapekey may have a
* different topology than the evaluated mesh. */
- BLI_assert(mesh->key == NULL || DEG_is_evaluated_id(&mesh->key->id));
+ BLI_assert(mesh->key == nullptr || DEG_is_evaluated_id(&mesh->key->id));
mesh_eval->key = mesh->key;
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
@@ -1846,11 +1937,14 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph,
Mesh *me_cage;
Mesh *me_final;
+ GeometrySet *non_mesh_components;
- editbmesh_calc_modifiers(depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final);
+ editbmesh_calc_modifiers(
+ depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components);
em->mesh_eval_final = me_final;
em->mesh_eval_cage = me_cage;
+ obedit->runtime.geometry_set_eval = non_mesh_components;
BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final);
@@ -1878,7 +1972,8 @@ static void object_get_datamask(const Depsgraph *depsgraph,
return;
}
- Object *actob = view_layer->basact ? DEG_get_original_object(view_layer->basact->object) : NULL;
+ Object *actob = view_layer->basact ? DEG_get_original_object(view_layer->basact->object) :
+ nullptr;
if (DEG_get_original_object(ob) == actob) {
bool editing = BKE_paint_select_face_test(actob);
@@ -1948,7 +2043,7 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping);
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
- if ((mesh_eval == NULL) ||
+ if ((mesh_eval == nullptr) ||
!CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) ||
(need_mapping && !ob->runtime.last_need_mapping)) {
CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask);
@@ -1957,7 +2052,7 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph,
mesh_eval = BKE_object_get_evaluated_mesh(ob);
}
- if (mesh_eval != NULL) {
+ if (mesh_eval != nullptr) {
BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL));
}
return mesh_eval;
@@ -2001,7 +2096,8 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph,
{
Mesh *final;
- mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, -1, false, false, NULL, &final);
+ mesh_calc_modifiers(
+ depsgraph, scene, ob, 1, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}
@@ -2014,7 +2110,8 @@ Mesh *mesh_create_eval_final_index_render(Depsgraph *depsgraph,
{
Mesh *final;
- mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, index, false, false, NULL, &final);
+ mesh_calc_modifiers(
+ depsgraph, scene, ob, 1, false, dataMask, index, false, false, nullptr, &final, nullptr);
return final;
}
@@ -2026,7 +2123,8 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph,
{
Mesh *final;
- mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, NULL, &final);
+ mesh_calc_modifiers(
+ depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}
@@ -2038,7 +2136,8 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph,
{
Mesh *final;
- mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, NULL, &final);
+ mesh_calc_modifiers(
+ depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr);
return final;
}
@@ -2058,7 +2157,7 @@ Mesh *editbmesh_get_eval_cage_and_final(Depsgraph *depsgraph,
/* if there's no derived mesh or the last data mask used doesn't include
* the data we need, rebuild the derived mesh
*/
- object_get_datamask(depsgraph, obedit, &cddata_masks, NULL);
+ object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr);
if (!em->mesh_eval_cage ||
!CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) {
@@ -2083,7 +2182,7 @@ Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph,
/* if there's no derived mesh or the last data mask used doesn't include
* the data we need, rebuild the derived mesh
*/
- object_get_datamask(depsgraph, obedit, &cddata_masks, NULL);
+ object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr);
if (!em->mesh_eval_cage ||
!CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) {
@@ -2108,10 +2207,10 @@ Mesh *editbmesh_get_eval_cage_from_orig(struct Depsgraph *depsgraph,
/***/
/* same as above but for vert coords */
-typedef struct {
+struct MappedUserData {
float (*vertexcos)[3];
BLI_bitmap *vertex_visit;
-} MappedUserData;
+};
static void make_vertexcos__mapFunc(void *userData,
int index,
@@ -2153,30 +2252,32 @@ void DM_calc_loop_tangents(DerivedMesh *dm,
const char (*tangent_names)[MAX_NAME],
int tangent_names_len)
{
- BKE_mesh_calc_loop_tangent_ex(dm->getVertArray(dm),
- dm->getPolyArray(dm),
- dm->getNumPolys(dm),
- dm->getLoopArray(dm),
- dm->getLoopTriArray(dm),
- dm->getNumLoopTri(dm),
- &dm->loopData,
- calc_active_tangent,
- tangent_names,
- tangent_names_len,
- CustomData_get_layer(&dm->polyData, CD_NORMAL),
- dm->getLoopDataArray(dm, CD_NORMAL),
- dm->getVertDataArray(dm, CD_ORCO), /* may be NULL */
- /* result */
- &dm->loopData,
- dm->getNumLoops(dm),
- &dm->tangent_mask);
+ BKE_mesh_calc_loop_tangent_ex(
+ dm->getVertArray(dm),
+ dm->getPolyArray(dm),
+ dm->getNumPolys(dm),
+ dm->getLoopArray(dm),
+ dm->getLoopTriArray(dm),
+ dm->getNumLoopTri(dm),
+ &dm->loopData,
+ calc_active_tangent,
+ tangent_names,
+ tangent_names_len,
+ (float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
+ (float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
+ (float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
+ /* result */
+ &dm->loopData,
+ dm->getNumLoops(dm),
+ &dm->tangent_mask);
}
static void mesh_init_origspace(Mesh *mesh)
{
const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
- OrigSpaceLoop *lof_array = CustomData_get_layer(&mesh->ldata, CD_ORIGSPACE_MLOOP);
+ OrigSpaceLoop *lof_array = (OrigSpaceLoop *)CustomData_get_layer(&mesh->ldata,
+ CD_ORIGSPACE_MLOOP);
const int numpoly = mesh->totpoly;
// const int numloop = mesh->totloop;
MVert *mv = mesh->mvert;
@@ -2184,8 +2285,7 @@ static void mesh_init_origspace(Mesh *mesh)
MPoly *mp = mesh->mpoly;
int i, j, k;
- float(*vcos_2d)[2] = NULL;
- BLI_array_staticdeclare(vcos_2d, 64);
+ blender::Vector<blender::float2, 64> vcos_2d;
for (i = 0; i < numpoly; i++, mp++) {
OrigSpaceLoop *lof = lof_array + mp->loopstart;
@@ -2206,8 +2306,7 @@ static void mesh_init_origspace(Mesh *mesh)
BKE_mesh_calc_poly_normal(mp, l, mv, p_nor);
axis_dominant_v3_to_m3(mat, p_nor);
- BLI_array_clear(vcos_2d);
- BLI_array_reserve(vcos_2d, mp->totloop);
+ vcos_2d.resize(mp->totloop);
for (j = 0; j < mp->totloop; j++, l++) {
mul_v3_m3v3(co, mat, mv[l->v].co);
copy_v2_v2(vcos_2d[j], co);
@@ -2245,7 +2344,6 @@ static void mesh_init_origspace(Mesh *mesh)
}
BKE_mesh_tessface_clear(mesh);
- BLI_array_free(vcos_2d);
}
/* derivedmesh info printing function,
@@ -2367,7 +2465,7 @@ bool DM_is_valid(DerivedMesh *dm)
do_fixes,
&changed);
- is_valid &= BKE_mesh_validate_arrays(NULL,
+ is_valid &= BKE_mesh_validate_arrays(nullptr,
dm->getVertArray(dm),
dm->getNumVerts(dm),
dm->getEdgeArray(dm),
@@ -2378,7 +2476,7 @@ bool DM_is_valid(DerivedMesh *dm)
dm->getNumLoops(dm),
dm->getPolyArray(dm),
dm->getNumPolys(dm),
- dm->getVertDataArray(dm, CD_MDEFORMVERT),
+ (MDeformVert *)dm->getVertDataArray(dm, CD_MDEFORMVERT),
do_verbose,
do_fixes,
&changed);
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 13abfa92032..633d6202222 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -42,6 +42,7 @@
#include "DNA_ID.h"
#include "DNA_anim_types.h"
#include "DNA_light_types.h"
+#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 03c812b3b3d..ea41495d097 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1189,7 +1189,7 @@ static void nlaeval_free(NlaEvalData *nlaeval)
/* ---------------------- */
-static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index)
+static int nlaevalchan_validate_index(const NlaEvalChannel *nec, int index)
{
if (nec->is_array) {
if (index >= 0 && index < nec->base_snapshot.length) {
@@ -1201,6 +1201,28 @@ static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index)
return 0;
}
+static bool nlaevalchan_validate_index_ex(const NlaEvalChannel *nec, const int array_index)
+{
+ /** Although array_index comes from fcurve, that doesn't necessarily mean the property has that
+ * many elements. */
+ const int index = nlaevalchan_validate_index(nec, array_index);
+
+ if (index < 0) {
+ if (G.debug & G_DEBUG) {
+ ID *id = nec->key.ptr.owner_id;
+ CLOG_WARN(&LOG,
+ "Animation: Invalid array index. ID = '%s', '%s[%d]', array length is %d",
+ id ? (id->name + 2) : "<No ID>",
+ nec->rna_path,
+ array_index,
+ nec->base_snapshot.length);
+ }
+
+ return false;
+ }
+ return true;
+}
+
/* Initialize default values for NlaEvalChannel from the property data. */
static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values)
{
@@ -1455,7 +1477,7 @@ static float nla_combine_value(
return old_value + (value - base_value) * inf;
case NEC_MIX_MULTIPLY:
- if (base_value == 0.0f) {
+ if (IS_EQF(base_value, 0.0f)) {
base_value = 1.0f;
}
return old_value * powf(value / base_value, inf);
@@ -1471,6 +1493,11 @@ static float nla_combine_value(
static bool nla_invert_blend_value(
int blend_mode, float old_value, float target_value, float influence, float *r_value)
{
+ /** No solution if strip had 0 influence. */
+ if (IS_EQF(influence, 0.0f)) {
+ return false;
+ }
+
switch (blend_mode) {
case NLASTRIP_MODE_ADD:
*r_value = (target_value - old_value) / influence;
@@ -1481,9 +1508,9 @@ static bool nla_invert_blend_value(
return true;
case NLASTRIP_MODE_MULTIPLY:
- if (old_value == 0.0f) {
+ if (IS_EQF(old_value, 0.0f)) {
/* Resolve 0/0 to 1. */
- if (target_value == 0.0f) {
+ if (IS_EQF(target_value, 0.0f)) {
*r_value = 1.0f;
return true;
}
@@ -1514,6 +1541,11 @@ static bool nla_invert_combine_value(int mix_mode,
float influence,
float *r_value)
{
+ /* No solution if strip had no influence. */
+ if (IS_EQF(influence, 0.0f)) {
+ return false;
+ }
+
switch (mix_mode) {
case NEC_MIX_ADD:
case NEC_MIX_AXIS_ANGLE:
@@ -1521,12 +1553,12 @@ static bool nla_invert_combine_value(int mix_mode,
return true;
case NEC_MIX_MULTIPLY:
- if (base_value == 0.0f) {
+ if (IS_EQF(base_value, 0.0f)) {
base_value = 1.0f;
}
- if (old_value == 0.0f) {
+ if (IS_EQF(old_value, 0.0f)) {
/* Resolve 0/0 to 1. */
- if (target_value == 0.0f) {
+ if (IS_EQF(target_value, 0.0f)) {
*r_value = base_value;
return true;
}
@@ -1560,11 +1592,14 @@ static void nla_combine_quaternion(const float old_values[4],
}
/* invert accumulation of quaternion channels for Combine mode according to influence */
-static void nla_invert_combine_quaternion(const float old_values[4],
+static bool nla_invert_combine_quaternion(const float old_values[4],
const float values[4],
float influence,
float result[4])
{
+ if (IS_EQF(influence, 0.0f)) {
+ return false;
+ }
float tmp_old[4], tmp_new[4];
normalize_qt_qt(tmp_old, old_values);
@@ -1573,6 +1608,8 @@ static void nla_invert_combine_quaternion(const float old_values[4],
mul_qt_qtqt(result, tmp_old, tmp_new);
pow_qt_fl_normalized(result, 1.0f / influence);
+
+ return true;
}
/* Data about the current blend mode. */
@@ -1612,19 +1649,7 @@ static bool nlaeval_blend_value(NlaBlendData *blend,
return false;
}
- int index = nlaevalchan_validate_index(nec, array_index);
-
- if (index < 0) {
- if (G.debug & G_DEBUG) {
- ID *id = nec->key.ptr.owner_id;
- CLOG_WARN(&LOG,
- "Animato: Invalid array index. ID = '%s', '%s[%d]', array length is %d",
- id ? (id->name + 2) : "<No ID>",
- nec->rna_path,
- array_index,
- nec->base_snapshot.length);
- }
-
+ if (!nlaevalchan_validate_index_ex(nec, array_index)) {
return false;
}
@@ -1633,21 +1658,21 @@ static bool nlaeval_blend_value(NlaBlendData *blend,
BLI_bitmap_set_all(nec->valid.ptr, true, 4);
}
else {
- BLI_BITMAP_ENABLE(nec->valid.ptr, index);
+ BLI_BITMAP_ENABLE(nec->valid.ptr, array_index);
}
NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec);
- float *p_value = &nec_snapshot->values[index];
+ float *p_value = &nec_snapshot->values[array_index];
if (blend->mode == NLASTRIP_MODE_COMBINE) {
/* Quaternion blending is deferred until all sub-channel values are known. */
if (nec->mix_mode == NEC_MIX_QUATERNION) {
NlaEvalChannelSnapshot *blend_snapshot = nlaevalchan_queue_blend(blend, nec);
- blend_snapshot->values[index] = value;
+ blend_snapshot->values[array_index] = value;
}
else {
- float base_value = nec->base_snapshot.values[index];
+ float base_value = nec->base_snapshot.values[array_index];
*p_value = nla_combine_value(nec->mix_mode, base_value, *p_value, value, blend->influence);
}
@@ -2502,7 +2527,9 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
*r_force_all = true;
- nla_invert_combine_quaternion(old_values, values, influence, values);
+ if (!nla_invert_combine_quaternion(old_values, values, influence, values)) {
+ return false;
+ }
}
else {
float *base_values = nec->base_snapshot.values;
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index b1462167edd..ae0c27635a6 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -36,6 +36,8 @@
#include "BKE_appdir.h" /* own include */
#include "BKE_blender_version.h"
+#include "BLT_translation.h"
+
#include "GHOST_Path-api.h"
#include "MEM_guardedalloc.h"
@@ -146,47 +148,74 @@ static char *blender_version_decimal(const int version)
* \{ */
/**
- * This is now only used to really get the user's default document folder.
+ * Get the folder that's the "natural" starting point for browsing files on an OS. On Unix that is
+ * $HOME, on Windows it is %userprofile%/Documents.
*
- * \note On Windows `Users/{MyUserName}/Documents` is used
- * as it's the default location to save documents.
+ * \note On Windows `Users/{MyUserName}/Documents` is used as it's the default location to save
+ * documents.
*/
const char *BKE_appdir_folder_default(void)
{
#ifndef WIN32
- const char *const xdg_documents_dir = BLI_getenv("XDG_DOCUMENTS_DIR");
+ return BLI_getenv("HOME");
+#else /* Windows */
+ static char documentfolder[MAXPATHLEN];
- if (xdg_documents_dir) {
- return xdg_documents_dir;
+ if (BKE_appdir_folder_documents(documentfolder)) {
+ return documentfolder;
}
+ return NULL;
+#endif /* WIN32 */
+}
+
+/**
+ * Get the user's home directory, i.e. $HOME on UNIX, %userprofile% on Windows.
+ */
+const char *BKE_appdir_folder_home(void)
+{
+#ifndef WIN32
return BLI_getenv("HOME");
-#else /* Windows */
- static char documentfolder[MAXPATHLEN];
- HRESULT hResult;
+#else /* Windows */
+ return BLI_getenv("userprofile");
+#endif
+}
- /* Check for `%HOME%` environment variable. */
- if (uput_getenv("HOME", documentfolder, MAXPATHLEN)) {
- if (BLI_is_dir(documentfolder)) {
- return documentfolder;
- }
+/**
+ * Get the user's document directory, i.e. $HOME/Documents on Linux, %userprofile%/Documents on
+ * Windows. If this can't be found using OS queries (via Ghost), try manually finding it.
+ *
+ * \returns True if the path is valid and points to an existing directory.
+ */
+bool BKE_appdir_folder_documents(char *dir)
+{
+ dir[0] = '\0';
+
+ const char *documents_path = (const char *)GHOST_getUserSpecialDir(
+ GHOST_kUserSpecialDirDocuments);
+
+ /* Usual case: Ghost gave us the documents path. We're done here. */
+ if (documents_path && BLI_is_dir(documents_path)) {
+ BLI_strncpy(dir, documents_path, FILE_MAXDIR);
+ return true;
}
- /* Add user profile support for WIN 2K / NT.
- * This is `%APPDATA%`, which translates to either:
- * - `%USERPROFILE%\Application Data` or...
- * - `%USERPROFILE%\AppData\Roaming` (since Vista).
- */
- hResult = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documentfolder);
+ /* Ghost couldn't give us a documents path, let's try if we can find it ourselves.*/
- if (hResult == S_OK) {
- if (BLI_is_dir(documentfolder)) {
- return documentfolder;
- }
+ const char *home_path = BKE_appdir_folder_home();
+ if (!home_path || !BLI_is_dir(home_path)) {
+ return false;
}
- return NULL;
-#endif /* WIN32 */
+ char try_documents_path[FILE_MAXDIR];
+ /* Own attempt at getting a valid Documents path. */
+ BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"), NULL);
+ if (!BLI_is_dir(try_documents_path)) {
+ return false;
+ }
+
+ BLI_strncpy(dir, try_documents_path, FILE_MAXDIR);
+ return true;
}
/**
@@ -877,14 +906,20 @@ bool BKE_appdir_program_python_search(char *fullpath,
const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME);
#endif
const char *basename = "python";
+#if defined(WIN32) && !defined(NDEBUG)
+ const char *basename_debug = "python_d";
+#endif
char python_version[16];
/* Check both possible names. */
const char *python_names[] = {
#ifdef PYTHON_EXECUTABLE_NAME
- python_build_def,
+ python_build_def,
+#endif
+#if defined(WIN32) && !defined(NDEBUG)
+ basename_debug,
#endif
- python_version,
- basename,
+ python_version,
+ basename,
};
bool is_found = false;
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 92146082557..ced211b1926 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -2441,7 +2441,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
}
/**
- * \param r_last_visited_bone_p the last bone handled by the last call to this function.
+ * \param r_last_visited_bone_p: The last bone handled by the last call to this function.
*/
static int rebuild_pose_bone(
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
diff --git a/source/blender/blenkernel/intern/asset.c b/source/blender/blenkernel/intern/asset.c
new file mode 100644
index 00000000000..7ccb0aa2b57
--- /dev/null
+++ b/source/blender/blenkernel/intern/asset.c
@@ -0,0 +1,151 @@
+/*
+ * 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 <string.h>
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_asset.h"
+#include "BKE_icons.h"
+#include "BKE_idprop.h"
+
+#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+#include "DNA_defaults.h"
+
+#include "BLO_read_write.h"
+
+#include "MEM_guardedalloc.h"
+
+AssetMetaData *BKE_asset_metadata_create(void)
+{
+ AssetMetaData *asset_data = MEM_callocN(sizeof(*asset_data), __func__);
+ memcpy(asset_data, DNA_struct_default_get(AssetMetaData), sizeof(*asset_data));
+ return asset_data;
+}
+
+void BKE_asset_metadata_free(AssetMetaData **asset_data)
+{
+ if ((*asset_data)->properties) {
+ IDP_FreeProperty((*asset_data)->properties);
+ }
+ MEM_SAFE_FREE((*asset_data)->description);
+ BLI_freelistN(&(*asset_data)->tags);
+
+ MEM_SAFE_FREE(*asset_data);
+}
+
+static AssetTag *asset_metadata_tag_add(AssetMetaData *asset_data, const char *const name)
+{
+ AssetTag *tag = MEM_callocN(sizeof(*tag), __func__);
+ BLI_strncpy(tag->name, name, sizeof(tag->name));
+
+ BLI_addtail(&asset_data->tags, tag);
+ asset_data->tot_tags++;
+ /* Invariant! */
+ BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
+
+ return tag;
+}
+
+AssetTag *BKE_asset_metadata_tag_add(AssetMetaData *asset_data, const char *name)
+{
+ AssetTag *tag = asset_metadata_tag_add(asset_data, name);
+ BLI_uniquename(&asset_data->tags, tag, name, '.', offsetof(AssetTag, name), sizeof(tag->name));
+ return tag;
+}
+
+/**
+ * Make sure there is a tag with name \a name, create one if needed.
+ */
+struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(AssetMetaData *asset_data,
+ const char *name)
+{
+ struct AssetTagEnsureResult result = {.tag = NULL};
+ if (!name[0]) {
+ return result;
+ }
+
+ AssetTag *tag = BLI_findstring(&asset_data->tags, name, offsetof(AssetTag, name));
+
+ if (tag) {
+ result.tag = tag;
+ result.is_new = false;
+ return result;
+ }
+
+ tag = asset_metadata_tag_add(asset_data, name);
+
+ result.tag = tag;
+ result.is_new = true;
+ return result;
+}
+
+void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag)
+{
+ BLI_assert(BLI_findindex(&asset_data->tags, tag) >= 0);
+ BLI_freelinkN(&asset_data->tags, tag);
+ asset_data->tot_tags--;
+ /* Invariant! */
+ BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
+}
+
+/* Queries -------------------------------------------- */
+
+PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
+ const ID *id)
+{
+ return BKE_previewimg_id_get(id);
+}
+
+/* .blend file API -------------------------------------------- */
+
+void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data)
+{
+ BLO_write_struct(writer, AssetMetaData, asset_data);
+
+ if (asset_data->properties) {
+ IDP_BlendWrite(writer, asset_data->properties);
+ }
+
+ if (asset_data->description) {
+ BLO_write_string(writer, asset_data->description);
+ }
+ LISTBASE_FOREACH (AssetTag *, tag, &asset_data->tags) {
+ BLO_write_struct(writer, AssetTag, tag);
+ }
+}
+
+void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
+{
+ /* asset_data itself has been read already. */
+
+ if (asset_data->properties) {
+ BLO_read_data_address(reader, &asset_data->properties);
+ IDP_BlendDataRead(reader, &asset_data->properties);
+ }
+
+ BLO_read_data_address(reader, &asset_data->description);
+ BLO_read_list(reader, &asset_data->tags);
+ BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
+}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index d79168d5443..934beb8a848 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -392,6 +392,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
return &CPPType::get<int>();
case CD_PROP_COLOR:
return &CPPType::get<Color4f>();
+ case CD_PROP_BOOL:
+ return &CPPType::get<bool>();
default:
return nullptr;
}
@@ -415,6 +417,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<Color4f>()) {
return CD_PROP_COLOR;
}
+ if (type.is<bool>()) {
+ return CD_PROP_BOOL;
+ }
return static_cast<CustomDataType>(-1);
}
@@ -449,6 +454,9 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom
case CD_PROP_COLOR:
return std::make_unique<ArrayReadAttribute<Color4f>>(
domain, Span(static_cast<Color4f *>(layer.data), size));
+ case CD_PROP_BOOL:
+ return std::make_unique<ArrayReadAttribute<bool>>(
+ domain, Span(static_cast<bool *>(layer.data), size));
}
}
}
@@ -490,6 +498,9 @@ static WriteAttributePtr write_attribute_from_custom_data(
case CD_PROP_COLOR:
return std::make_unique<ArrayWriteAttribute<Color4f>>(
domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
+ case CD_PROP_BOOL:
+ return std::make_unique<ArrayWriteAttribute<bool>>(
+ domain, MutableSpan(static_cast<bool *>(layer.data), size));
}
}
}
@@ -590,6 +601,15 @@ Set<std::string> GeometryComponent::attribute_names() const
return {};
}
+bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
+{
+ ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ if (attribute) {
+ return true;
+ }
+ return false;
+}
+
static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
const blender::fn::CPPType &to_type)
{
@@ -640,6 +660,28 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
return attribute;
}
+ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
+ const AttributeDomain domain) const
+{
+ if (!this->attribute_domain_supported(domain)) {
+ return {};
+ }
+
+ ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
+ }
+
+ if (attribute->domain() != domain) {
+ attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
+ if (!attribute) {
+ return {};
+ }
+ }
+
+ return attribute;
+}
+
ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
@@ -742,6 +784,7 @@ bool PointCloudComponent::attribute_domain_with_type_supported(
const AttributeDomain domain, const CustomDataType data_type) const
{
return domain == ATTR_DOMAIN_POINT && ELEM(data_type,
+ CD_PROP_BOOL,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
@@ -865,8 +908,13 @@ bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain d
if (!this->attribute_domain_supported(domain)) {
return false;
}
- return ELEM(
- data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
+ return ELEM(data_type,
+ CD_PROP_BOOL,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT2,
+ CD_PROP_FLOAT3,
+ CD_PROP_INT32,
+ CD_PROP_COLOR);
}
int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index f4f25c3a153..5b5bd416ef2 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -296,6 +296,7 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
}
BLI_freelistN(&userdef->autoexec_paths);
+ BLI_freelistN(&userdef->asset_libraries);
BLI_freelistN(&userdef->uistyles);
BLI_freelistN(&userdef->uifonts);
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 567773507cf..0855db1a943 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -52,6 +52,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -645,6 +646,8 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
/* Default studio light. */
BKE_studiolight_default(userdef->light_param, userdef->light_ambient);
+ BKE_preferences_asset_library_default_add(userdef);
+
return userdef;
}
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
index 96bf9fbe8d2..e69173cc1d5 100644
--- a/source/blender/blenkernel/intern/boids.c
+++ b/source/blender/blenkernel/intern/boids.c
@@ -39,6 +39,7 @@
#include "BKE_collision.h"
#include "BKE_effect.h"
#include "BKE_particle.h"
+#include "BLI_kdopbvh.h"
#include "BKE_modifier.h"
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 96791aed2c3..9a954a89cad 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -23,6 +23,7 @@
#include "DNA_brush_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -1833,6 +1834,14 @@ void BKE_brush_sculpt_reset(Brush *br)
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_SPHERE;
break;
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
+ br->alpha = 1.0f;
+ br->spacing = 5;
+ br->hardness = 0.7f;
+ br->flag &= ~BRUSH_ALPHA_PRESSURE;
+ br->flag &= ~BRUSH_SPACE_ATTEN;
+ br->curve_preset = BRUSH_CURVE_SMOOTHER;
+ break;
default:
break;
}
@@ -1897,6 +1906,7 @@ void BKE_brush_sculpt_reset(Brush *br)
case SCULPT_TOOL_MASK:
case SCULPT_TOOL_DRAW_FACE_SETS:
case SCULPT_TOOL_DISPLACEMENT_ERASER:
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
br->add_col[0] = 0.75f;
br->add_col[1] = 0.75f;
br->add_col[2] = 0.75f;
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 5eec788255d..b86b59066d6 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -1773,6 +1773,15 @@ static void layer_collection_flags_store(Main *bmain,
}
}
+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)
{
@@ -1788,7 +1797,6 @@ static void layer_collection_flags_restore_recursive(LayerCollection *layer_coll
child_flag = child_flag->next;
}
- BLI_freelistN(&flag->children);
/* We treat exclude as a special case.
*
@@ -1814,10 +1822,15 @@ static void layer_collection_flags_restore(ListBase *flags, const Collection *co
LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(
view_layer, collection);
- /* The flags should only be added if the collection is in the view layer. */
- BLI_assert(layer_collection != NULL);
-
- layer_collection_flags_restore_recursive(layer_collection, flag);
+ /* 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);
@@ -1877,7 +1890,7 @@ bool BKE_collection_move(Main *bmain,
/* Create and remove layer collections. */
BKE_main_collection_sync(bmain);
- /* Restore the original layer collection flags. */
+ /* Restore the original layer collection flags and free their temporary storage. */
if (do_flag_sync) {
layer_collection_flags_restore(&layer_flags, collection);
}
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 1c17692ac36..b6f84dfc42f 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -5960,7 +5960,7 @@ static bConstraint *constraint_find_original_for_update(bConstraintOb *cob, bCon
* Check whether given constraint is not local (i.e. from linked data) when the object is a library
* override.
*
- * \param con May be NULL, in which case we consider it as a non-local constraint case.
+ * \param con: May be NULL, in which case we consider it as a non-local constraint case.
*/
bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstraint *con)
{
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index a1edfd1c56d..65accc66084 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -123,7 +123,7 @@ void CTX_free(bContext *C)
/* store */
-bContextStore *CTX_store_add(ListBase *contexts, const char *name, PointerRNA *ptr)
+bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr)
{
/* ensure we have a context to put the entry in, if it was already used
* we have to copy the context to ensure */
@@ -178,6 +178,11 @@ bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context)
return ctx;
}
+bContextStore *CTX_store_get(bContext *C)
+{
+ return C->wm.store;
+}
+
void CTX_store_set(bContext *C, bContextStore *store)
{
C->wm.store = store;
@@ -1202,6 +1207,11 @@ ToolSettings *CTX_data_tool_settings(const bContext *C)
return NULL;
}
+int CTX_data_selected_ids(const bContext *C, ListBase *list)
+{
+ return ctx_data_collection_get(C, "selected_ids", list);
+}
+
int CTX_data_selected_nodes(const bContext *C, ListBase *list)
{
return ctx_data_collection_get(C, "selected_nodes", list);
diff --git a/source/blender/blenkernel/intern/cryptomatte.c b/source/blender/blenkernel/intern/cryptomatte.c
deleted file mode 100644
index 6570ffce920..00000000000
--- a/source/blender/blenkernel/intern/cryptomatte.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2008 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup bke
- */
-
-#include "BKE_cryptomatte.h"
-
-#include "DNA_material_types.h"
-#include "DNA_object_types.h"
-
-#include "BLI_compiler_attrs.h"
-#include "BLI_hash_mm3.h"
-#include "BLI_string.h"
-#include <string.h>
-
-static uint32_t cryptomatte_hash(const ID *id)
-{
- const char *name = &id->name[2];
- const int len = BLI_strnlen(name, MAX_NAME - 2);
- uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, len, 0);
- return cryptohash_int;
-}
-
-uint32_t BKE_cryptomatte_object_hash(const Object *object)
-{
- return cryptomatte_hash(&object->id);
-}
-
-uint32_t BKE_cryptomatte_material_hash(const Material *material)
-{
- if (material == NULL) {
- return 0.0f;
- }
- return cryptomatte_hash(&material->id);
-}
-
-uint32_t BKE_cryptomatte_asset_hash(const Object *object)
-{
- const Object *asset_object = object;
- while (asset_object->parent != NULL) {
- asset_object = asset_object->parent;
- }
- return cryptomatte_hash(&asset_object->id);
-}
-
-/* Convert a cryptomatte hash to a float.
- *
- * Cryptomatte hashes are stored in float textures and images. The conversion is taken from the
- * cryptomatte specification. See Floating point conversion section in
- * https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf.
- *
- * The conversion uses as many 32 bit floating point values as possible to minimize hash
- * collisions. Unfortunately not all 32 bits can be as NaN and Inf can be problematic.
- *
- * Note that this conversion assumes to be running on a L-endian system. */
-float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
-{
- uint32_t mantissa = cryptomatte_hash & ((1 << 23) - 1);
- uint32_t exponent = (cryptomatte_hash >> 23) & ((1 << 8) - 1);
- exponent = MAX2(exponent, (uint32_t)1);
- exponent = MIN2(exponent, (uint32_t)254);
- exponent = exponent << 23;
- uint32_t sign = (cryptomatte_hash >> 31);
- sign = sign << 31;
- uint32_t float_bits = sign | exponent | mantissa;
- float f;
- memcpy(&f, &float_bits, sizeof(uint32_t));
- return f;
-}
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
new file mode 100644
index 00000000000..4bbeb088628
--- /dev/null
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -0,0 +1,190 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_cryptomatte.h"
+#include "BKE_main.h"
+
+#include "DNA_material_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_dynstr.h"
+#include "BLI_hash_mm3.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "MEM_guardedalloc.h"
+
+#include <cstring>
+#include <sstream>
+#include <string>
+
+static uint32_t cryptomatte_hash(const ID *id)
+{
+ const char *name = &id->name[2];
+ const int name_len = BLI_strnlen(name, MAX_NAME);
+ uint32_t cryptohash_int = BKE_cryptomatte_hash(name, name_len);
+ return cryptohash_int;
+}
+
+uint32_t BKE_cryptomatte_hash(const char *name, int name_len)
+{
+ uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, name_len, 0);
+ return cryptohash_int;
+}
+
+uint32_t BKE_cryptomatte_object_hash(const Object *object)
+{
+ return cryptomatte_hash(&object->id);
+}
+
+uint32_t BKE_cryptomatte_material_hash(const Material *material)
+{
+ if (material == nullptr) {
+ return 0.0f;
+ }
+ return cryptomatte_hash(&material->id);
+}
+
+uint32_t BKE_cryptomatte_asset_hash(const Object *object)
+{
+ const Object *asset_object = object;
+ while (asset_object->parent != nullptr) {
+ asset_object = asset_object->parent;
+ }
+ return cryptomatte_hash(&asset_object->id);
+}
+
+/* Convert a cryptomatte hash to a float.
+ *
+ * Cryptomatte hashes are stored in float textures and images. The conversion is taken from the
+ * cryptomatte specification. See Floating point conversion section in
+ * https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf.
+ *
+ * The conversion uses as many 32 bit floating point values as possible to minimize hash
+ * collisions. Unfortunately not all 32 bits can be as NaN and Inf can be problematic.
+ *
+ * Note that this conversion assumes to be running on a L-endian system. */
+float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
+{
+ uint32_t mantissa = cryptomatte_hash & ((1 << 23) - 1);
+ uint32_t exponent = (cryptomatte_hash >> 23) & ((1 << 8) - 1);
+ exponent = MAX2(exponent, (uint32_t)1);
+ exponent = MIN2(exponent, (uint32_t)254);
+ exponent = exponent << 23;
+ uint32_t sign = (cryptomatte_hash >> 31);
+ sign = sign << 31;
+ uint32_t float_bits = sign | exponent | mantissa;
+ float f;
+ memcpy(&f, &float_bits, sizeof(uint32_t));
+ return f;
+}
+
+static ID *cryptomatte_find_id(const ListBase *ids, const float encoded_hash)
+{
+ LISTBASE_FOREACH (ID *, id, ids) {
+ uint32_t hash = BKE_cryptomatte_hash((id->name + 2), BLI_strnlen(id->name + 2, MAX_NAME));
+ if (BKE_cryptomatte_hash_to_float(hash) == encoded_hash) {
+ return id;
+ }
+ }
+ return nullptr;
+}
+
+/* Find an ID in the given main that matches the given encoded float. */
+static struct ID *BKE_cryptomatte_find_id(const Main *bmain, const float encoded_hash)
+{
+ ID *result;
+ result = cryptomatte_find_id(&bmain->objects, encoded_hash);
+ if (result == nullptr) {
+ result = cryptomatte_find_id(&bmain->materials, encoded_hash);
+ }
+ return result;
+}
+
+char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
+{
+ DynStr *matte_id = BLI_dynstr_new();
+ bool first = true;
+ LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) {
+ if (!first) {
+ BLI_dynstr_append(matte_id, ",");
+ }
+ if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) {
+ BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name));
+ }
+ else {
+ BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash);
+ }
+ first = false;
+ }
+ char *result = BLI_dynstr_get_cstring(matte_id);
+ BLI_dynstr_free(matte_id);
+ return result;
+}
+
+void BKE_cryptomatte_matte_id_to_entries(const Main *bmain,
+ NodeCryptomatte *node_storage,
+ const char *matte_id)
+{
+ BLI_freelistN(&node_storage->entries);
+
+ std::istringstream ss(matte_id);
+ while (ss.good()) {
+ CryptomatteEntry *entry = nullptr;
+ std::string token;
+ getline(ss, token, ',');
+ /* Ignore empty tokens. */
+ if (token.length() > 0) {
+ size_t first = token.find_first_not_of(' ');
+ size_t last = token.find_last_not_of(' ');
+ if (first == std::string::npos || last == std::string::npos) {
+ break;
+ }
+ token = token.substr(first, (last - first + 1));
+ if (*token.begin() == '<' && *(--token.end()) == '>') {
+ float encoded_hash = atof(token.substr(1, token.length() - 2).c_str());
+ entry = (CryptomatteEntry *)MEM_callocN(sizeof(CryptomatteEntry), __func__);
+ entry->encoded_hash = encoded_hash;
+ if (bmain) {
+ ID *id = BKE_cryptomatte_find_id(bmain, encoded_hash);
+ if (id != nullptr) {
+ BLI_strncpy(entry->name, id->name + 2, sizeof(entry->name));
+ }
+ }
+ }
+ else {
+ const char *name = token.c_str();
+ int name_len = token.length();
+ entry = (CryptomatteEntry *)MEM_callocN(sizeof(CryptomatteEntry), __func__);
+ BLI_strncpy(entry->name, name, sizeof(entry->name));
+ uint32_t hash = BKE_cryptomatte_hash(name, name_len);
+ entry->encoded_hash = BKE_cryptomatte_hash_to_float(hash);
+ }
+ }
+ if (entry != nullptr) {
+ BLI_addtail(&node_storage->entries, entry);
+ }
+ }
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index fdb3e246382..1e2bc570c65 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -1837,6 +1837,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerMultiply_propfloat2,
NULL,
layerAdd_propfloat2},
+ /* 50: CD_PROP_POOL */
+ {sizeof(bool),
+ "bool",
+ 1,
+ N_("Boolean"),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -1892,6 +1907,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropCol",
"CDPropFloat3",
"CDPropFloat2",
+ "CDPropBoolean",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index b56a15b3d45..e18b2d87459 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -42,6 +42,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 9431915b4e4..df1dbaa905f 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -125,11 +125,17 @@ static void vfont_free_data(ID *id)
static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
VFont *vf = (VFont *)id;
- if (vf->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (vf->id.us > 0 || is_undo) {
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
vf->data = NULL;
vf->temp_pf = NULL;
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) {
+ vf->packedfile = NULL;
+ }
+
/* write LibData */
BLO_write_id_struct(writer, VFont, id_address, &vf->id);
BKE_id_blend_write(writer, &vf->id);
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 28695d769d3..e6a67b191f8 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -460,31 +460,54 @@ GeometryComponent *InstancesComponent::copy() const
new_component->positions_ = positions_;
new_component->rotations_ = rotations_;
new_component->scales_ = scales_;
- new_component->objects_ = objects_;
+ new_component->instanced_data_ = instanced_data_;
return new_component;
}
void InstancesComponent::clear()
{
- objects_.clear();
+ instanced_data_.clear();
positions_.clear();
rotations_.clear();
scales_.clear();
}
-void InstancesComponent::add_instance(const Object *object,
+
+void InstancesComponent::add_instance(Object *object,
+ blender::float3 position,
+ blender::float3 rotation,
+ blender::float3 scale)
+{
+ InstancedData data;
+ data.type = INSTANCE_DATA_TYPE_OBJECT;
+ data.data.object = object;
+ this->add_instance(data, position, rotation, scale);
+}
+
+void InstancesComponent::add_instance(Collection *collection,
+ blender::float3 position,
+ blender::float3 rotation,
+ blender::float3 scale)
+{
+ InstancedData data;
+ data.type = INSTANCE_DATA_TYPE_COLLECTION;
+ data.data.collection = collection;
+ this->add_instance(data, position, rotation, scale);
+}
+
+void InstancesComponent::add_instance(InstancedData data,
blender::float3 position,
blender::float3 rotation,
blender::float3 scale)
{
- objects_.append(object);
+ instanced_data_.append(data);
positions_.append(position);
rotations_.append(rotation);
scales_.append(scale);
}
-Span<const Object *> InstancesComponent::objects() const
+Span<InstancedData> InstancesComponent::instanced_data() const
{
- return objects_;
+ return instanced_data_;
}
Span<float3> InstancesComponent::positions() const
@@ -509,8 +532,11 @@ MutableSpan<float3> InstancesComponent::positions()
int InstancesComponent::instances_amount() const
{
- BLI_assert(positions_.size() == objects_.size());
- return objects_.size();
+ const int size = instanced_data_.size();
+ BLI_assert(positions_.size() == size);
+ BLI_assert(rotations_.size() == size);
+ BLI_assert(scales_.size() == size);
+ return size;
}
bool InstancesComponent::is_empty() const
@@ -538,7 +564,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set,
float (**r_positions)[3],
float (**r_rotations)[3],
float (**r_scales)[3],
- Object ***r_objects)
+ InstancedData **r_instanced_data)
{
const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
if (component == nullptr) {
@@ -547,7 +573,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set,
*r_positions = (float(*)[3])component->positions().data();
*r_rotations = (float(*)[3])component->rotations().data();
*r_scales = (float(*)[3])component->scales().data();
- *r_objects = (Object **)component->objects().data();
+ *r_instanced_data = (InstancedData *)component->instanced_data().data();
return component->instances_amount();
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 8cc11468771..be14d74de7a 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -36,8 +36,11 @@
#include "BLT_translation.h"
+#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
#include "BKE_collection.h"
#include "BKE_context.h"
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index fc74439fd76..981f5d50353 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -34,6 +34,7 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_hash.h"
+#include "BLI_heap.h"
#include "BLI_math_vector.h"
#include "BLI_polyfill_2d.h"
@@ -41,6 +42,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
@@ -3224,4 +3226,197 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
}
}
+
+/* Stroke Uniform Subdivide ------------------------------------- */
+
+typedef 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 {
+ 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__);
+ 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) {
+ new_pt->totweight = dvert->totweight;
+ new_pt->dw = 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];
+ dw->def_nr = dw_from->def_nr;
+ dw->weight = dw_from->weight;
+ }
+ }
+ return new_pt;
+}
+
+/* Helper: creates a tSampleEdge from two tSamplePoints. Also calculates the length (squared) of
+ * the edge. */
+static tSampleEdge *new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to)
+{
+ tSampleEdge *new_edge = MEM_callocN(sizeof(tSampleEdge), __func__);
+ new_edge->from = from;
+ new_edge->to = to;
+ new_edge->length_sq = len_squared_v3v3(&from->x, &to->x);
+ return new_edge;
+}
+
+/**
+ * Subdivide the grease pencil stroke so the number of points is target_number.
+ * Does not change the shape of the stroke. The new points will be distributed as
+ * uniformly as possible by repeatedly subdividing the current longest edge.
+ *
+ * \param gps: The stroke to be up-sampled.
+ * \param target_number: The number of points the up-sampled stroke should have.
+ * \param select: Select/Deselect the stroke.
+ */
+void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
+ bGPDstroke *gps,
+ const uint32_t target_number,
+ 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) {
+ return;
+ }
+
+ const int totpoints = gps->totpoints;
+ const bool has_dverts = (gps->dvert != NULL);
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
+
+ ListBase points = {NULL, NULL};
+ 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;
+ 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) {
+ 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. */
+ BLI_heap_insert(edges, -(se->length_sq), se);
+ }
+
+ if (is_cyclic) {
+ tSamplePoint *sp_first = points.first;
+ tSamplePoint *sp_last = points.last;
+ tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first);
+ BLI_heap_insert(edges, -(se->length_sq), se);
+ }
+
+ int num_points_needed = target_number - totpoints;
+ BLI_assert(num_points_needed > 0);
+
+ while (num_points_needed > 0) {
+ tSampleEdge *se = 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__);
+ 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);
+ new_sp->time = interpf(sp->time, sp_next->time, 0.5f);
+ interp_v4_v4v4((float *)&new_sp->vertex_color,
+ (float *)&sp->vertex_color,
+ (float *)&sp_next->vertex_color,
+ 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__);
+ for (uint32_t i = 0; i < new_sp->totweight; ++i) {
+ MDeformWeight *dw = &new_sp->dw[i];
+ MDeformWeight *dw_from = &sp->dw[i];
+ MDeformWeight *dw_to = &sp_next->dw[i];
+ dw->def_nr = dw_from->def_nr;
+ dw->weight = interpf(dw_from->weight, dw_to->weight, 0.5f);
+ }
+ }
+ BLI_insertlinkafter(&points, sp, new_sp);
+
+ tSampleEdge *se_prev = new_sample_edge_from_sample_points(sp, new_sp);
+ tSampleEdge *se_next = new_sample_edge_from_sample_points(new_sp, sp_next);
+ BLI_heap_insert(edges, -(se_prev->length_sq), se_prev);
+ BLI_heap_insert(edges, -(se_next->length_sq), se_next);
+
+ MEM_freeN(se);
+ num_points_needed--;
+ }
+
+ /* Edges are no longer needed. Heap is freed. */
+ BLI_heap_free(edges, (HeapFreeFP)MEM_freeN);
+
+ gps->totpoints = target_number;
+ gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
+ if (has_dverts) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
+ }
+
+ /* Convert list back to stroke point array. */
+ tSamplePoint *sp = 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];
+
+ copy_v3_v3(&pt->x, &sp->x);
+ pt->pressure = sp->pressure;
+ pt->strength = sp->strength;
+ pt->time = sp->time;
+ copy_v4_v4((float *)&pt->vert_color, (float *)&sp->vertex_color);
+
+ if (sp->dw) {
+ dvert->totweight = sp->totweight;
+ dvert->dw = 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];
+ dw->def_nr = dw_from->def_nr;
+ dw->weight = dw_from->weight;
+ }
+ }
+ if (select) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ }
+
+ if (select) {
+ gps->flag |= GP_STROKE_SELECT;
+ }
+
+ /* 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) {
+ MEM_freeN(temp->dw);
+ }
+ MEM_SAFE_FREE(temp);
+ }
+
+ /* Update the geometry of the stroke. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index ac81e4a5470..1be2cba31b5 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -534,7 +534,7 @@ void BKE_gpencil_modifier_set_error(GpencilModifierData *md, const char *_format
* Check whether given modifier is not local (i.e. from linked data) when the object is a library
* override.
*
- * \param gmd May be NULL, in which case we consider it as a non-local modifier case.
+ * \param gmd: May be NULL, in which case we consider it as a non-local modifier case.
*/
bool BKE_gpencil_modifier_is_nonlocal_in_liboverride(const Object *ob,
const GpencilModifierData *gmd)
diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.cc
index eec9013d067..fbf69357478 100644
--- a/source/blender/blenkernel/intern/icons.c
+++ b/source/blender/blenkernel/intern/icons.cc
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -21,9 +21,10 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
#include "CLG_log.h"
@@ -46,6 +47,7 @@
#include "BLI_string.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_global.h" /* only for G.background test */
#include "BKE_icons.h"
@@ -61,6 +63,8 @@
#include "BLO_read_write.h"
+#include "atomic_ops.h"
+
/**
* Only allow non-managed icons to be removed (by Python for eg).
* Previews & ID's have their own functions to remove icons.
@@ -73,28 +77,35 @@ enum {
static CLG_LogRef LOG = {"bke.icons"};
-static GHash *gIcons = NULL;
+/* Protected by gIconMutex. */
+static GHash *gIcons = nullptr;
+/* Protected by gIconMutex. */
static int gNextIconId = 1;
+/* Protected by gIconMutex. */
static int gFirstIconId = 1;
-static GHash *gCachedPreviews = NULL;
+std::mutex gIconMutex;
+
+/* Not mutex-protected! */
+static GHash *gCachedPreviews = nullptr;
/* Queue of icons for deferred deletion. */
typedef struct DeferredIconDeleteNode {
struct DeferredIconDeleteNode *next;
int icon_id;
} DeferredIconDeleteNode;
+/* Protected by gIconMutex. */
static LockfreeLinkList g_icon_delete_queue;
static void icon_free(void *val)
{
- Icon *icon = val;
+ Icon *icon = (Icon *)val;
if (icon) {
if (icon->obj_type == ICON_DATA_GEOM) {
- struct Icon_Geom *obj = icon->obj;
+ struct Icon_Geom *obj = (struct Icon_Geom *)icon->obj;
if (obj->mem) {
/* coords & colors are part of this memory. */
MEM_freeN((void *)obj->mem);
@@ -121,6 +132,12 @@ static void icon_free_data(int icon_id, Icon *icon)
if (icon->obj_type == ICON_DATA_ID) {
((ID *)(icon->obj))->icon_id = 0;
}
+ else if (icon->obj_type == ICON_DATA_IMBUF) {
+ ImBuf *imbuf = (ImBuf *)icon->obj;
+ if (imbuf) {
+ IMB_freeImBuf(imbuf);
+ }
+ }
else if (icon->obj_type == ICON_DATA_PREVIEW) {
((PreviewImage *)(icon->obj))->icon_id = 0;
}
@@ -131,8 +148,8 @@ static void icon_free_data(int icon_id, Icon *icon)
((struct Icon_Geom *)(icon->obj))->icon_id = 0;
}
else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) {
- StudioLight *sl = icon->obj;
- if (sl != NULL) {
+ StudioLight *sl = (StudioLight *)icon->obj;
+ if (sl != nullptr) {
BKE_studiolight_unset_icon_id(sl, icon_id);
}
}
@@ -141,19 +158,27 @@ static void icon_free_data(int icon_id, Icon *icon)
}
}
+static Icon *icon_ghash_lookup(int icon_id)
+{
+ std::scoped_lock lock(gIconMutex);
+ return (Icon *)BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
+}
+
/* create an id for a new icon and make sure that ids from deleted icons get reused
* after the integer number range is used up */
-static int get_next_free_id(void)
+static int get_next_free_id()
{
- BLI_assert(BLI_thread_is_main());
+ std::scoped_lock lock(gIconMutex);
int startId = gFirstIconId;
/* if we haven't used up the int number range, we just return the next int */
if (gNextIconId >= gFirstIconId) {
- return gNextIconId++;
+ int next_id = gNextIconId++;
+ return next_id;
}
- /* now we try to find the smallest icon id not stored in the gIcons hash */
+ /* Now we try to find the smallest icon id not stored in the gIcons hash.
+ * Don't use icon_ghash_lookup here, it would lock recursively (dead-lock). */
while (BLI_ghash_lookup(gIcons, POINTER_FROM_INT(startId)) && startId >= gFirstIconId) {
startId++;
}
@@ -189,13 +214,13 @@ void BKE_icons_free(void)
BLI_assert(BLI_thread_is_main());
if (gIcons) {
- BLI_ghash_free(gIcons, NULL, icon_free);
- gIcons = NULL;
+ BLI_ghash_free(gIcons, nullptr, icon_free);
+ gIcons = nullptr;
}
if (gCachedPreviews) {
BLI_ghash_free(gCachedPreviews, MEM_freeN, BKE_previewimg_freefunc);
- gCachedPreviews = NULL;
+ gCachedPreviews = nullptr;
}
BLI_linklist_lockfree_free(&g_icon_delete_queue, MEM_freeN);
@@ -203,20 +228,21 @@ void BKE_icons_free(void)
void BKE_icons_deferred_free(void)
{
- BLI_assert(BLI_thread_is_main());
+ std::scoped_lock lock(gIconMutex);
for (DeferredIconDeleteNode *node =
(DeferredIconDeleteNode *)BLI_linklist_lockfree_begin(&g_icon_delete_queue);
- node != NULL;
+ node != nullptr;
node = node->next) {
- BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), NULL, icon_free);
+ BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), nullptr, icon_free);
}
BLI_linklist_lockfree_clear(&g_icon_delete_queue, MEM_freeN);
}
static PreviewImage *previewimg_create_ex(size_t deferred_data_size)
{
- PreviewImage *prv_img = MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, "img_prv");
+ PreviewImage *prv_img = (PreviewImage *)MEM_mallocN(sizeof(PreviewImage) + deferred_data_size,
+ "img_prv");
memset(prv_img, 0, sizeof(*prv_img)); /* leave deferred data dirty */
if (deferred_data_size) {
@@ -224,12 +250,26 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size)
}
for (int i = 0; i < NUM_ICON_SIZES; i++) {
- prv_img->flag[i] |= PRV_CHANGED;
+ prv_img->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED);
prv_img->changed_timestamp[i] = 0;
}
return prv_img;
}
+static PreviewImage *previewimg_deferred_create(const char *path, int source)
+{
+ /* We pack needed data for lazy loading (source type, in a single char, and path). */
+ const size_t deferred_data_size = strlen(path) + 2;
+ char *deferred_data;
+
+ PreviewImage *prv = previewimg_create_ex(deferred_data_size);
+ deferred_data = (char *)PRV_DEFERRED_DATA(prv);
+ deferred_data[0] = source;
+ memcpy(&deferred_data[1], path, deferred_data_size - 1);
+
+ return prv;
+}
+
PreviewImage *BKE_previewimg_create(void)
{
return previewimg_create_ex(0);
@@ -256,7 +296,7 @@ void BKE_previewimg_free(PreviewImage **prv)
{
if (prv && (*prv)) {
BKE_previewimg_freefunc(*prv);
- *prv = NULL;
+ *prv = nullptr;
}
}
@@ -267,7 +307,7 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
GPU_texture_free(prv->gputexture[size]);
}
prv->h[size] = prv->w[size] = 0;
- prv->flag[size] |= PRV_CHANGED;
+ prv->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED);
prv->flag[size] &= ~PRV_USER_EDITED;
prv->changed_timestamp[size] = 0;
}
@@ -275,21 +315,21 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
void BKE_previewimg_clear(struct PreviewImage *prv)
{
for (int i = 0; i < NUM_ICON_SIZES; i++) {
- BKE_previewimg_clear_single(prv, i);
+ BKE_previewimg_clear_single(prv, (eIconSizes)i);
}
}
PreviewImage *BKE_previewimg_copy(const PreviewImage *prv)
{
- PreviewImage *prv_img = NULL;
+ PreviewImage *prv_img = nullptr;
if (prv) {
- prv_img = MEM_dupallocN(prv);
+ prv_img = (PreviewImage *)MEM_dupallocN(prv);
for (int i = 0; i < NUM_ICON_SIZES; i++) {
if (prv->rect[i]) {
- prv_img->rect[i] = MEM_dupallocN(prv->rect[i]);
+ prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]);
}
- prv_img->gputexture[i] = NULL;
+ prv_img->gputexture[i] = nullptr;
}
}
return prv_img;
@@ -305,7 +345,7 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id)
PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id);
if (old_prv_p && *old_prv_p) {
- BLI_assert(new_prv_p != NULL && ELEM(*new_prv_p, NULL, *old_prv_p));
+ BLI_assert(new_prv_p != nullptr && ELEM(*new_prv_p, nullptr, *old_prv_p));
// const int new_icon_id = get_next_free_id();
// if (new_icon_id == 0) {
@@ -339,7 +379,13 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id)
break;
}
- return NULL;
+ return nullptr;
+}
+
+PreviewImage *BKE_previewimg_id_get(const ID *id)
+{
+ PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
+ return prv_p ? *prv_p : nullptr;
}
void BKE_previewimg_id_free(ID *id)
@@ -355,18 +401,60 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id)
PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
if (prv_p) {
- if (*prv_p == NULL) {
+ if (*prv_p == nullptr) {
*prv_p = BKE_previewimg_create();
}
return *prv_p;
}
- return NULL;
+ return nullptr;
+}
+
+void BKE_previewimg_id_custom_set(ID *id, const char *path)
+{
+ PreviewImage **prv = BKE_previewimg_id_get_p(id);
+
+ /* Thumbnail previews must use the deferred pipeline. But we force them to be immediately
+ * generated here still. */
+
+ if (*prv) {
+ BKE_previewimg_deferred_release(*prv);
+ }
+ *prv = previewimg_deferred_create(path, THB_SOURCE_IMAGE);
+
+ /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be
+ * there in time. Not only if something happened to have accessed it meanwhile. */
+ for (int i = 0; i < NUM_ICON_SIZES; i++) {
+ BKE_previewimg_ensure(*prv, i);
+ /* Prevent auto-updates. */
+ (*prv)->flag[i] |= PRV_USER_EDITED;
+ }
+}
+
+bool BKE_previewimg_id_supports_jobs(const ID *id)
+{
+ return ELEM(GS(id->name), ID_OB, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM, ID_BR);
+}
+
+void BKE_previewimg_deferred_release(PreviewImage *prv)
+{
+ if (prv) {
+ if (prv->tag & PRV_TAG_DEFFERED_RENDERING) {
+ /* We cannot delete the preview while it is being loaded in another thread... */
+ prv->tag |= PRV_TAG_DEFFERED_DELETE;
+ return;
+ }
+ if (prv->icon_id) {
+ BKE_icon_delete(prv->icon_id);
+ }
+ BKE_previewimg_freefunc(prv);
+ }
}
PreviewImage *BKE_previewimg_cached_get(const char *name)
{
- return BLI_ghash_lookup(gCachedPreviews, name);
+ BLI_assert(BLI_thread_is_main());
+ return (PreviewImage *)BLI_ghash_lookup(gCachedPreviews, name);
}
/**
@@ -374,14 +462,16 @@ PreviewImage *BKE_previewimg_cached_get(const char *name)
*/
PreviewImage *BKE_previewimg_cached_ensure(const char *name)
{
- PreviewImage *prv = NULL;
+ BLI_assert(BLI_thread_is_main());
+
+ PreviewImage *prv = nullptr;
void **key_p, **prv_p;
if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) {
*key_p = BLI_strdup(name);
*prv_p = BKE_previewimg_create();
}
- prv = *prv_p;
+ prv = *(PreviewImage **)prv_p;
BLI_assert(prv);
return prv;
@@ -389,24 +479,27 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name)
/**
* Generate a PreviewImage from given file path, using thumbnails management, if not yet existing.
+ * Does not actually generate the preview, #BKE_previewimg_ensure() must be called for that.
*/
PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name,
const char *path,
const int source,
bool force_update)
{
- PreviewImage *prv = NULL;
+ BLI_assert(BLI_thread_is_main());
+
+ PreviewImage *prv = nullptr;
void **prv_p;
prv_p = BLI_ghash_lookup_p(gCachedPreviews, name);
if (prv_p) {
- prv = *prv_p;
+ prv = *(PreviewImage **)prv_p;
BLI_assert(prv);
}
if (prv && force_update) {
- const char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
+ const char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv);
if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) {
/* If same path, no need to re-allocate preview, just clear it up. */
BKE_previewimg_clear(prv);
@@ -417,15 +510,7 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name,
}
if (!prv) {
- /* We pack needed data for lazy loading (source type, in a single char, and path). */
- const size_t deferred_data_size = strlen(path) + 2;
- char *deferred_data;
-
- prv = previewimg_create_ex(deferred_data_size);
- deferred_data = PRV_DEFERRED_DATA(prv);
- deferred_data[0] = source;
- memcpy(&deferred_data[1], path, deferred_data_size - 1);
-
+ prv = previewimg_deferred_create(path, source);
force_update = true;
}
@@ -441,26 +526,13 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name,
return prv;
}
-void BKE_previewimg_cached_release_pointer(PreviewImage *prv)
-{
- if (prv) {
- if (prv->tag & PRV_TAG_DEFFERED_RENDERING) {
- /* We cannot delete the preview while it is being loaded in another thread... */
- prv->tag |= PRV_TAG_DEFFERED_DELETE;
- return;
- }
- if (prv->icon_id) {
- BKE_icon_delete(prv->icon_id);
- }
- BKE_previewimg_freefunc(prv);
- }
-}
-
void BKE_previewimg_cached_release(const char *name)
{
- PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN);
+ BLI_assert(BLI_thread_is_main());
+
+ PreviewImage *prv = (PreviewImage *)BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN);
- BKE_previewimg_cached_release_pointer(prv);
+ BKE_previewimg_deferred_release(prv);
}
/**
@@ -475,12 +547,12 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size)
if (do_icon || do_preview) {
ImBuf *thumb;
- char *prv_deferred_data = PRV_DEFERRED_DATA(prv);
+ char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv);
int source = prv_deferred_data[0];
char *path = &prv_deferred_data[1];
int icon_w, icon_h;
- thumb = IMB_thumb_manage(path, THB_LARGE, source);
+ thumb = IMB_thumb_manage(path, THB_LARGE, (ThumbSource)source);
if (thumb) {
/* PreviewImage assumes premultiplied alhpa... */
@@ -489,8 +561,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size)
if (do_preview) {
prv->w[ICON_SIZE_PREVIEW] = thumb->x;
prv->h[ICON_SIZE_PREVIEW] = thumb->y;
- prv->rect[ICON_SIZE_PREVIEW] = MEM_dupallocN(thumb->rect);
- prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED);
+ prv->rect[ICON_SIZE_PREVIEW] = (uint *)MEM_dupallocN(thumb->rect);
+ prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED);
}
if (do_icon) {
if (thumb->x > thumb->y) {
@@ -508,8 +580,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size)
IMB_scaleImBuf(thumb, icon_w, icon_h);
prv->w[ICON_SIZE_ICON] = icon_w;
prv->h[ICON_SIZE_ICON] = icon_h;
- prv->rect[ICON_SIZE_ICON] = MEM_dupallocN(thumb->rect);
- prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED);
+ prv->rect[ICON_SIZE_ICON] = (uint *)MEM_dupallocN(thumb->rect);
+ prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED);
}
IMB_freeImBuf(thumb);
}
@@ -517,13 +589,45 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size)
}
}
+/**
+ * Create an #ImBuf holding a copy of the preview image buffer in \a prv.
+ * \note The returned image buffer has to be free'd (#IMB_freeImBuf()).
+ */
+ImBuf *BKE_previewimg_to_imbuf(PreviewImage *prv, const int size)
+{
+ const unsigned int w = prv->w[size];
+ const unsigned int h = prv->h[size];
+ const unsigned int *rect = prv->rect[size];
+
+ ImBuf *ima = nullptr;
+
+ if (w > 0 && h > 0 && rect) {
+ /* first allocate imbuf for copying preview into it */
+ ima = IMB_allocImBuf(w, h, 32, IB_rect);
+ memcpy(ima->rect, rect, w * h * sizeof(*ima->rect));
+ }
+
+ return ima;
+}
+
+void BKE_previewimg_finish(PreviewImage *prv, const int size)
+{
+ /* Previews may be calculated on a thread. */
+ atomic_fetch_and_and_int16(&prv->flag[size], ~PRV_UNFINISHED);
+}
+
+bool BKE_previewimg_is_finished(const PreviewImage *prv, const int size)
+{
+ return (prv->flag[size] & PRV_UNFINISHED) == 0;
+}
+
void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
{
/* Note we write previews also for undo steps. It takes up some memory,
* but not doing so would causes all previews to be re-rendered after
* undo which is too expensive. */
- if (prv == NULL) {
+ if (prv == nullptr) {
return;
}
@@ -532,7 +636,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
if (!(U.flag & USER_SAVE_PREVIEWS)) {
prv_copy.w[1] = 0;
prv_copy.h[1] = 0;
- prv_copy.rect[1] = NULL;
+ prv_copy.rect[1] = nullptr;
}
BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
if (prv_copy.rect[0]) {
@@ -545,7 +649,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
{
- if (prv == NULL) {
+ if (prv == nullptr) {
return;
}
@@ -553,7 +657,18 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
if (prv->rect[i]) {
BLO_read_data_address(reader, &prv->rect[i]);
}
- prv->gputexture[i] = NULL;
+ prv->gputexture[i] = nullptr;
+ /* For now consider previews read from file as finished to not confuse File Browser preview
+ * loading. That could be smarter and check if there's a preview job running instead.
+ * If the preview is tagged as changed, it needs to be updated anyway, so don't remove the tag.
+ */
+ if ((prv->flag[i] & PRV_CHANGED) == 0) {
+ BKE_previewimg_finish(prv, i);
+ }
+ else {
+ /* Only for old files that didn't write the flag . */
+ prv->flag[i] |= PRV_UNFINISHED;
+ }
}
prv->icon_id = 0;
prv->tag = 0;
@@ -561,15 +676,13 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv)
void BKE_icon_changed(const int icon_id)
{
- BLI_assert(BLI_thread_is_main());
-
- Icon *icon = NULL;
+ Icon *icon = nullptr;
if (!icon_id || G.background) {
return;
}
- icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
+ icon = icon_ghash_lookup(icon_id);
if (icon) {
/* We *only* expect ID-tied icons here, not non-ID icon/preview! */
@@ -584,7 +697,7 @@ void BKE_icon_changed(const int icon_id)
/* If we have previews, they all are now invalid changed. */
if (p_prv && *p_prv) {
for (int i = 0; i < NUM_ICON_SIZES; i++) {
- (*p_prv)->flag[i] |= PRV_CHANGED;
+ (*p_prv)->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED);
(*p_prv)->changed_timestamp[i]++;
}
}
@@ -593,7 +706,7 @@ void BKE_icon_changed(const int icon_id)
static Icon *icon_create(int icon_id, int obj_type, void *obj)
{
- Icon *new_icon = MEM_mallocN(sizeof(Icon), __func__);
+ Icon *new_icon = (Icon *)MEM_mallocN(sizeof(Icon), __func__);
new_icon->obj_type = obj_type;
new_icon->obj = obj;
@@ -601,10 +714,13 @@ static Icon *icon_create(int icon_id, int obj_type, void *obj)
new_icon->flag = 0;
/* next two lines make sure image gets created */
- new_icon->drawinfo = NULL;
- new_icon->drawinfo_free = NULL;
+ new_icon->drawinfo = nullptr;
+ new_icon->drawinfo_free = nullptr;
- BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon);
+ {
+ std::scoped_lock lock(gIconMutex);
+ BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon);
+ }
return new_icon;
}
@@ -731,17 +847,47 @@ int BKE_icon_preview_ensure(ID *id, PreviewImage *preview)
return preview->icon_id;
}
+/**
+ * Create an icon as owner or \a ibuf. The icon-ID is not stored in \a ibuf, it needs to be stored
+ * separately.
+ * \note Transforms ownership of \a ibuf to the newly created icon.
+ */
+int BKE_icon_imbuf_create(ImBuf *ibuf)
+{
+ int icon_id = get_next_free_id();
+
+ Icon *icon = icon_create(icon_id, ICON_DATA_IMBUF, ibuf);
+ icon->flag = ICON_FLAG_MANAGED;
+
+ return icon_id;
+}
+
+ImBuf *BKE_icon_imbuf_get_buffer(int icon_id)
+{
+ Icon *icon = icon_ghash_lookup(icon_id);
+ if (!icon) {
+ CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id);
+ return nullptr;
+ }
+ if (icon->obj_type != ICON_DATA_IMBUF) {
+ CLOG_ERROR(&LOG, "icon ID does not refer to an imbuf icon: %d", icon_id);
+ return nullptr;
+ }
+
+ return (ImBuf *)icon->obj;
+}
+
Icon *BKE_icon_get(const int icon_id)
{
BLI_assert(BLI_thread_is_main());
- Icon *icon = NULL;
+ Icon *icon = nullptr;
- icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id));
+ icon = icon_ghash_lookup(icon_id);
if (!icon) {
CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id);
- return NULL;
+ return nullptr;
}
return icon;
@@ -749,10 +895,9 @@ Icon *BKE_icon_get(const int icon_id)
void BKE_icon_set(const int icon_id, struct Icon *icon)
{
- BLI_assert(BLI_thread_is_main());
-
void **val_p;
+ std::scoped_lock lock(gIconMutex);
if (BLI_ghash_ensure_p(gIcons, POINTER_FROM_INT(icon_id), &val_p)) {
CLOG_ERROR(&LOG, "icon already set: %d", icon_id);
return;
@@ -763,8 +908,10 @@ void BKE_icon_set(const int icon_id, struct Icon *icon)
static void icon_add_to_deferred_delete_queue(int icon_id)
{
- DeferredIconDeleteNode *node = MEM_mallocN(sizeof(DeferredIconDeleteNode), __func__);
+ DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)MEM_mallocN(
+ sizeof(DeferredIconDeleteNode), __func__);
node->icon_id = icon_id;
+ /* Doesn't need lock. */
BLI_linklist_lockfree_insert(&g_icon_delete_queue, (LockfreeLinkNode *)node);
}
@@ -782,7 +929,8 @@ void BKE_icon_id_delete(struct ID *id)
}
BKE_icons_deferred_free();
- BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), NULL, icon_free);
+ std::scoped_lock lock(gIconMutex);
+ BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), nullptr, icon_free);
}
/**
@@ -795,8 +943,8 @@ bool BKE_icon_delete(const int icon_id)
return false;
}
- Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL);
- if (icon) {
+ std::scoped_lock lock(gIconMutex);
+ if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr)) {
icon_free_data(icon_id, icon);
icon_free(icon);
return true;
@@ -812,7 +960,9 @@ bool BKE_icon_delete_unmanaged(const int icon_id)
return false;
}
- Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL);
+ std::scoped_lock lock(gIconMutex);
+
+ Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr);
if (icon) {
if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) {
BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), icon);
@@ -847,52 +997,52 @@ int BKE_icon_geom_ensure(struct Icon_Geom *geom)
return geom->icon_id;
}
-struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len)
+struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len)
{
BLI_assert(BLI_thread_is_main());
if (data_len <= 8) {
- goto fail;
+ return nullptr;
}
+ /* Wrapper for RAII early exit cleanups. */
+ std::unique_ptr<uchar> data_wrapper(std::move(data));
+
/* Skip the header. */
data_len -= 8;
const int div = 3 * 2 * 3;
const int coords_len = data_len / div;
if (coords_len * div != data_len) {
- goto fail;
+ return nullptr;
}
const uchar header[4] = {'V', 'C', 'O', 0};
- const uchar *p = data;
+ uchar *p = data_wrapper.get();
if (memcmp(p, header, ARRAY_SIZE(header)) != 0) {
- goto fail;
+ return nullptr;
}
p += 4;
- struct Icon_Geom *geom = MEM_mallocN(sizeof(*geom), __func__);
+ struct Icon_Geom *geom = (struct Icon_Geom *)MEM_mallocN(sizeof(*geom), __func__);
geom->coords_range[0] = (int)*p++;
geom->coords_range[1] = (int)*p++;
/* x, y ignored for now */
p += 2;
geom->coords_len = coords_len;
- geom->coords = (void *)p;
- geom->colors = (void *)(p + (data_len / 3));
+ geom->coords = reinterpret_cast<decltype(geom->coords)>(p);
+ geom->colors = reinterpret_cast<decltype(geom->colors)>(p + (data_len / 3));
geom->icon_id = 0;
- geom->mem = data;
+ /* Move buffer ownership to C buffer. */
+ geom->mem = data_wrapper.release();
return geom;
-
-fail:
- MEM_freeN((void *)data);
- return NULL;
}
struct Icon_Geom *BKE_icon_geom_from_file(const char *filename)
{
BLI_assert(BLI_thread_is_main());
size_t data_len;
- uchar *data = BLI_file_read_binary_as_mem(filename, 0, &data_len);
- if (data == NULL) {
- return NULL;
+ uchar *data = (uchar *)BLI_file_read_binary_as_mem(filename, 0, &data_len);
+ if (data == nullptr) {
+ return nullptr;
}
return BKE_icon_geom_from_memory(data, data_len);
}
diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c
index f8a1113f69b..433f0e97844 100644
--- a/source/blender/blenkernel/intern/idprop_utils.c
+++ b/source/blender/blenkernel/intern/idprop_utils.c
@@ -26,6 +26,8 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "DNA_ID.h"
+
#include "BKE_idprop.h"
#include "BKE_idtype.h"
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index 44bf8f0e4db..1889d1c4eb0 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -36,6 +36,7 @@
#include "BLT_translation.h"
#include "DNA_ID.h"
+#include "DNA_collection_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index cd2ed32cd4f..228aed265cf 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -54,6 +54,7 @@
#include "DNA_camera_types.h"
#include "DNA_defaults.h"
#include "DNA_light_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -225,14 +226,21 @@ static void image_foreach_cache(ID *id,
static void image_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Image *ima = (Image *)id;
- if (ima->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (ima->id.us > 0 || is_undo) {
ImagePackedFile *imapf;
- /* Some trickery to keep forward compatibility of packed images. */
BLI_assert(ima->packedfile == NULL);
- if (ima->packedfiles.first != NULL) {
- imapf = ima->packedfiles.first;
- ima->packedfile = imapf->packedfile;
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) {
+ BLI_listbase_clear(&ima->packedfiles);
+ }
+ else {
+ /* Some trickery to keep forward compatibility of packed images. */
+ if (ima->packedfiles.first != NULL) {
+ imapf = ima->packedfiles.first;
+ ima->packedfile = imapf->packedfile;
+ }
}
/* write LibData */
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index 9ed233ab34c..50138b34fa3 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -62,7 +62,7 @@ bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf)
return ibuf->rect_float != NULL;
}
}
- else if (ibuf) {
+ if (ibuf) {
if (ibuf->rect_float) {
return image ? (image->alpha_mode != IMA_ALPHA_STRAIGHT) : false;
}
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index 6a852df95c6..0e611b21304 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -467,7 +467,9 @@ static char *shapekey_adrcodes_to_paths(ID *id, int adrcode, int *UNUSED(array_i
/* setting that we alter is the "value" (i.e. keyblock.curval) */
if (kb) {
/* Use the keyblock name, escaped, so that path lookups for this will work */
- BLI_snprintf(buf, sizeof(buf), "key_blocks[\"%s\"].value", kb->name);
+ char kb_name_esc[sizeof(kb->name) * 2];
+ BLI_str_escape(kb_name_esc, kb->name, sizeof(kb_name_esc));
+ BLI_snprintf(buf, sizeof(buf), "key_blocks[\"%s\"].value", kb_name_esc);
}
else {
/* Fallback - Use the adrcode as index directly, so that this can be manually fixed */
@@ -1118,7 +1120,7 @@ static char *get_rna_access(ID *id,
propname = "speed_fader";
break;
case SEQ_FAC_OPACITY:
- propname = "blend_opacity";
+ propname = "blend_alpha";
break;
}
/* XXX this doesn't seem to be included anywhere in sequencer RNA... */
@@ -1160,7 +1162,12 @@ static char *get_rna_access(ID *id,
/* note, strings are not escapted and they should be! */
if ((actname && actname[0]) && (constname && constname[0])) {
/* Constraint in Pose-Channel */
- BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"].constraints[\"%s\"]", actname, constname);
+ char actname_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
+ char constname_esc[sizeof(((bConstraint *)NULL)->name) * 2];
+ BLI_str_escape(actname_esc, actname, sizeof(actname_esc));
+ BLI_str_escape(constname_esc, constname, sizeof(constname_esc));
+ BLI_snprintf(
+ buf, sizeof(buf), "pose.bones[\"%s\"].constraints[\"%s\"]", actname_esc, constname_esc);
}
else if (actname && actname[0]) {
if ((blocktype == ID_OB) && STREQ(actname, "Object")) {
@@ -1174,16 +1181,22 @@ static char *get_rna_access(ID *id,
}
else {
/* Pose-Channel */
- BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"]", actname);
+ char actname_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
+ BLI_str_escape(actname_esc, actname, sizeof(actname_esc));
+ BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"]", actname_esc);
}
}
else if (constname && constname[0]) {
/* Constraint in Object */
- BLI_snprintf(buf, sizeof(buf), "constraints[\"%s\"]", constname);
+ char constname_esc[sizeof(((bConstraint *)NULL)->name) * 2];
+ BLI_str_escape(constname_esc, constname, sizeof(constname_esc));
+ BLI_snprintf(buf, sizeof(buf), "constraints[\"%s\"]", constname_esc);
}
else if (seq) {
/* Sequence names in Scene */
- BLI_snprintf(buf, sizeof(buf), "sequence_editor.sequences_all[\"%s\"]", seq->name + 2);
+ char seq_name_esc[(sizeof(seq->name) - 2) * 2];
+ BLI_str_escape(seq_name_esc, seq->name + 2, sizeof(seq_name_esc));
+ BLI_snprintf(buf, sizeof(buf), "sequence_editor.sequences_all[\"%s\"]", seq_name_esc);
}
else {
buf[0] = '\0'; /* empty string */
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 7468112b40e..433d64a5927 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -108,7 +108,8 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data)
static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Key *key = (Key *)id;
- if (key->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (key->id.us > 0 || is_undo) {
/* write LibData */
BLO_write_id_struct(writer, Key, id_address, &key->id);
BKE_id_blend_write(writer, &key->id);
@@ -119,9 +120,15 @@ static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_add
/* direct data */
LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
- BLO_write_struct(writer, KeyBlock, kb);
- if (kb->data) {
- BLO_write_raw(writer, kb->totelem * key->elemsize, kb->data);
+ KeyBlock tmp_kb = *kb;
+ /* Do not store actual geometry data in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) {
+ tmp_kb.totelem = 0;
+ tmp_kb.data = NULL;
+ }
+ BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb);
+ if (tmp_kb.data != NULL) {
+ BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data);
}
}
}
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 8a699e31f37..fbad0d920ed 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -2054,7 +2054,7 @@ static void bke_view_layer_verify_aov_cb(void *userdata,
const char *name,
int UNUSED(channels),
const char *UNUSED(chanid),
- int UNUSED(type))
+ eNodeSocketDatatype UNUSED(type))
{
GHash *name_count = userdata;
void **value_p;
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 21a77e2b45a..be7ce34f7e6 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -37,6 +37,7 @@
/* all types are needed here, in order to do memory operations */
#include "DNA_ID.h"
#include "DNA_anim_types.h"
+#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
#include "DNA_node_types.h"
@@ -55,6 +56,7 @@
#include "BKE_anim_data.h"
#include "BKE_armature.h"
+#include "BKE_asset.h"
#include "BKE_bpath.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -2264,6 +2266,12 @@ bool BKE_id_is_in_global_main(ID *id)
return (id == NULL || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1);
}
+bool BKE_id_can_be_asset(const ID *id)
+{
+ return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(id) &&
+ BKE_idtype_idcode_is_linkable(GS(id->name));
+}
+
/************************* Datablock order in UI **************************/
static int *id_order_get(ID *id)
@@ -2361,6 +2369,10 @@ void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after)
void BKE_id_blend_write(BlendWriter *writer, ID *id)
{
+ if (id->asset_data) {
+ BKE_asset_metadata_write(writer, id->asset_data);
+ }
+
/* ID_WM's id->properties are considered runtime only, and never written in .blend file. */
if (id->properties && !ELEM(GS(id->name), ID_WM)) {
IDP_BlendWrite(writer, id->properties);
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 1e45a3c1163..7199bd0f13c 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -31,6 +31,7 @@
#include "BLI_listbase.h"
#include "BKE_anim_data.h"
+#include "BKE_asset.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_key.h"
@@ -64,6 +65,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user)
id->override_library = NULL;
}
+ if (id->asset_data) {
+ BKE_asset_metadata_free(&id->asset_data);
+ }
+
BKE_animdata_free(id, do_id_user);
}
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index d82315a0e7f..cabc80d4024 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -369,6 +369,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
static bool lib_override_hierarchy_recursive_tag(Main *bmain,
ID *id,
const uint tag,
+ const uint missing_tag,
Library *override_group_lib_reference)
{
void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id);
@@ -377,9 +378,16 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain,
return (id->tag & tag) != 0;
}
+ /* Note: in case some reference ID is missing from linked data (and therefore its override uses
+ * a placeholder as reference), use `missing_tag` instead of `tag` for this override. */
if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
id->override_library->reference->lib == override_group_lib_reference) {
- id->tag |= tag;
+ if (id->override_library->reference->tag & LIB_TAG_MISSING) {
+ id->tag |= missing_tag;
+ }
+ else {
+ id->tag |= tag;
+ }
}
/* This way we won't process again that ID, should we encounter it again through another
@@ -397,7 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain,
/* We only consider IDs from the same library. */
if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
if (lib_override_hierarchy_recursive_tag(
- bmain, *entry->id_pointer, tag, override_group_lib_reference) &&
+ bmain, *entry->id_pointer, tag, missing_tag, override_group_lib_reference) &&
override_group_lib_reference == NULL) {
id->tag |= tag;
}
@@ -430,7 +438,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain,
/* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shape-key
* has a driver using an armature object's bone, we need to override the shape-key/obdata,
* the objects using them, etc.) */
- lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL);
+ lib_override_hierarchy_recursive_tag(bmain, id_root, tag, 0, NULL);
BKE_main_relations_free(bmain);
}
@@ -447,6 +455,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain,
void BKE_lib_override_library_override_group_tag(Main *bmain,
ID *id_root,
const uint tag,
+ const uint missing_tag,
const bool do_create_main_relashionships)
{
if (do_create_main_relashionships) {
@@ -456,7 +465,7 @@ void BKE_lib_override_library_override_group_tag(Main *bmain,
/* We tag all liboverride data-blocks from the same library as reference one,
* being used by the root ID. */
lib_override_hierarchy_recursive_tag(
- bmain, id_root, tag, id_root->override_library->reference->lib);
+ bmain, id_root, tag, missing_tag, id_root->override_library->reference->lib);
BKE_main_relations_free(bmain);
}
@@ -492,8 +501,9 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da
}
/* We tag all collections and objects for override. And we also tag all other data-blocks which
- * would use one of those. */
- if (ELEM(GS(id->name), ID_OB, ID_GR)) {
+ * would use one of those.
+ * Note: missing IDs (aka placeholders) are never overridden. */
+ if (ELEM(GS(id->name), ID_OB, ID_GR) && !(id->tag & LIB_TAG_MISSING)) {
id->tag |= LIB_TAG_DOIT;
}
@@ -692,6 +702,12 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
&ob_proxy->proxy->id;
ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
+ /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
+ * sure this is a valid state, but for now just abort the overriding process. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
+ return false;
+ }
+
/* We manually convert the proxy object into a library override, further override handling will
* then be handled by `BKE_lib_override_library_create()` just as for a regular override
* creation.
@@ -725,7 +741,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
/* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag
* linked reference ones to be overridden again. */
- BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true);
+ BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING, true);
GHash *linkedref_to_old_override = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
@@ -835,6 +851,12 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
}
id->tag &= ~LIB_TAG_DOIT;
}
+ /* Also cleanup old overrides that went missing in new linked data. */
+ else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ }
}
FOREACH_MAIN_ID_END;
BKE_id_multi_tagged_delete(bmain);
@@ -876,7 +898,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
id_root->tag |= LIB_TAG_DOIT;
/* Tag all library overrides in the chains of dependencies from the given root one. */
- BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true);
+ BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_DOIT, true);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 9e3189afee9..56f7bb0be6f 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -24,6 +24,7 @@
#include "BLI_utildefines.h"
+#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "BKE_armature.h"
@@ -342,7 +343,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o
static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id)
{
/* Update all group nodes using a node group. */
- ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id);
+ ntreeUpdateAllUsers(bmain, new_id);
}
/**
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 9ccdf5189d1..53f2a85fdad 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -173,24 +173,53 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data)
static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Mesh *mesh = (Mesh *)id;
- if (mesh->id.us > 0 || BLO_write_is_undo(writer)) {
- /* cache only - don't write */
- mesh->mface = NULL;
- mesh->totface = 0;
- memset(&mesh->fdata, 0, sizeof(mesh->fdata));
- memset(&mesh->runtime, 0, sizeof(mesh->runtime));
-
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (mesh->id.us > 0 || is_undo) {
CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
- CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
+ /* cache only - don't write */
+ mesh->mface = NULL;
+ mesh->totface = 0;
+ memset(&mesh->fdata, 0, sizeof(mesh->fdata));
+ memset(&mesh->runtime, 0, sizeof(mesh->runtime));
flayers = flayers_buff;
- CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
- CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+
+ /* Do not store actual geometry data in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) {
+ mesh->mvert = NULL;
+ mesh->totvert = 0;
+ memset(&mesh->vdata, 0, sizeof(mesh->vdata));
+ vlayers = vlayers_buff;
+
+ mesh->medge = NULL;
+ mesh->totedge = 0;
+ memset(&mesh->edata, 0, sizeof(mesh->edata));
+ elayers = elayers_buff;
+
+ mesh->mloop = NULL;
+ mesh->totloop = 0;
+ memset(&mesh->ldata, 0, sizeof(mesh->ldata));
+ llayers = llayers_buff;
+
+ mesh->mpoly = NULL;
+ mesh->totpoly = 0;
+ memset(&mesh->pdata, 0, sizeof(mesh->pdata));
+ players = players_buff;
+ }
+ else {
+ CustomData_blend_write_prepare(
+ &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
+ CustomData_blend_write_prepare(
+ &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
+ CustomData_blend_write_prepare(
+ &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
+ CustomData_blend_write_prepare(
+ &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+ }
BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
BKE_id_blend_write(writer, &mesh->id);
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 8272bd07411..73883afe19e 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1460,7 +1460,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
return result;
}
-/* This is a Mesh-based copy of the same function in DerivedMesh.c */
+/* This is a Mesh-based copy of the same function in DerivedMesh.cc */
static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid)
{
KeyBlock *kb;
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
new file mode 100644
index 00000000000..527288d06cf
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -0,0 +1,505 @@
+/*
+ * 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.
+ *
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_map.hh"
+#include "BLI_math.h"
+#include "BLI_vector.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
+#include "BKE_mesh_mapping.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "MEM_guardedalloc.h"
+#include "eigen_capi.h"
+
+using blender::Map;
+using blender::Vector;
+using std::array;
+
+class VertexWeight {
+ public:
+ virtual float weight_at_index(const int index) = 0;
+ virtual ~VertexWeight() = default;
+};
+
+class LoopWeight {
+ public:
+ virtual float weight_at_index(const int index) = 0;
+ virtual ~LoopWeight() = default;
+};
+
+class FairingContext {
+ public:
+ /* Get coordinates of vertices which are adjacent to the loop with specified index. */
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) = 0;
+
+ /* Get the other vertex index for a loop. */
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) = 0;
+
+ int vertex_count_get()
+ {
+ return totvert_;
+ }
+
+ int loop_count_get()
+ {
+ return totvert_;
+ }
+
+ MeshElemMap *vertex_loop_map_get(const int v)
+ {
+ return &vlmap_[v];
+ }
+
+ float *vertex_deformation_co_get(const int v)
+ {
+ return co_[v];
+ }
+
+ virtual ~FairingContext() = default;
+
+ void fair_vertices(bool *affected,
+ const eMeshFairingDepth depth,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+
+ fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight);
+ }
+
+ protected:
+ Vector<float *> co_;
+
+ int totvert_;
+ int totloop_;
+
+ MeshElemMap *vlmap_;
+ int *vlmap_mem_;
+
+ private:
+ void fair_setup_fairing(const int v,
+ const int i,
+ LinearSolver *solver,
+ float multiplier,
+ const int depth,
+ Map<int, int> &vert_col_map,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+ if (depth == 0) {
+ if (vert_col_map.contains(v)) {
+ const int j = vert_col_map.lookup(v);
+ EIG_linear_solver_matrix_add(solver, i, j, -multiplier);
+ return;
+ }
+ for (int j = 0; j < 3; j++) {
+ EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co_[v][j]);
+ }
+ return;
+ }
+
+ float w_ij_sum = 0;
+ const float w_i = vertex_weight->weight_at_index(v);
+ MeshElemMap *vlmap_elem = &vlmap_[v];
+ for (int l = 0; l < vlmap_elem->count; l++) {
+ const int l_index = vlmap_elem->indices[l];
+ const int other_vert = other_vertex_index_from_loop(l_index, v);
+ const float w_ij = loop_weight->weight_at_index(l_index);
+ w_ij_sum += w_ij;
+ fair_setup_fairing(other_vert,
+ i,
+ solver,
+ w_i * w_ij * multiplier,
+ depth - 1,
+ vert_col_map,
+ vertex_weight,
+ loop_weight);
+ }
+ fair_setup_fairing(v,
+ i,
+ solver,
+ -1 * w_i * w_ij_sum * multiplier,
+ depth - 1,
+ vert_col_map,
+ vertex_weight,
+ loop_weight);
+ }
+
+ void fair_vertices_ex(bool *affected,
+ const int order,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+ Map<int, int> vert_col_map;
+ int num_affected_vertices = 0;
+ for (int i = 0; i < totvert_; i++) {
+ if (!affected[i]) {
+ continue;
+ }
+ vert_col_map.add(i, num_affected_vertices);
+ num_affected_vertices++;
+ }
+
+ /* Early return, nothing to do. */
+ if (num_affected_vertices == 0 || num_affected_vertices == totvert_) {
+ return;
+ }
+
+ /* Setup fairing matrices */
+ LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3);
+ for (auto item : vert_col_map.items()) {
+ const int v = item.key;
+ const int col = item.value;
+ fair_setup_fairing(v, col, solver, 1.0f, order, vert_col_map, vertex_weight, loop_weight);
+ }
+
+ /* Solve linear system */
+ EIG_linear_solver_solve(solver);
+
+ /* Copy the result back to the mesh */
+ for (auto item : vert_col_map.items()) {
+ const int v = item.key;
+ const int col = item.value;
+ for (int j = 0; j < 3; j++) {
+ co_[v][j] = EIG_linear_solver_variable_get(solver, j, col);
+ }
+ }
+
+ /* Free solver data */
+ EIG_linear_solver_delete(solver);
+ }
+};
+
+class MeshFairingContext : public FairingContext {
+ public:
+ MeshFairingContext(Mesh *mesh, MVert *deform_mverts)
+ {
+ totvert_ = mesh->totvert;
+ totloop_ = mesh->totloop;
+
+ medge_ = mesh->medge;
+ mpoly_ = mesh->mpoly;
+ mloop_ = mesh->mloop;
+ BKE_mesh_vert_loop_map_create(&vlmap_,
+ &vlmap_mem_,
+ mesh->mpoly,
+ mesh->mloop,
+ mesh->totvert,
+ mesh->totpoly,
+ mesh->totloop);
+
+ /* Deformation coords. */
+ co_.reserve(mesh->totvert);
+ if (deform_mverts) {
+ for (int i = 0; i < mesh->totvert; i++) {
+ co_[i] = deform_mverts[i].co;
+ }
+ }
+ else {
+ for (int i = 0; i < mesh->totvert; i++) {
+ co_[i] = mesh->mvert[i].co;
+ }
+ }
+
+ loop_to_poly_map_.reserve(mesh->totloop);
+ for (int i = 0; i < mesh->totpoly; i++) {
+ for (int l = 0; l < mesh->mpoly[i].totloop; l++) {
+ loop_to_poly_map_[l + mesh->mpoly[i].loopstart] = i;
+ }
+ }
+ }
+
+ ~MeshFairingContext()
+ {
+ MEM_SAFE_FREE(vlmap_);
+ MEM_SAFE_FREE(vlmap_mem_);
+ }
+
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) override
+ {
+ const int vert = mloop_[loop].v;
+ const MPoly *p = &mpoly_[loop_to_poly_map_[loop]];
+ const int corner = poly_find_loop_from_vert(p, &mloop_[p->loopstart], vert);
+ copy_v3_v3(r_adj_next, co_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]);
+ copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]);
+ }
+
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override
+ {
+ MEdge *e = &medge_[mloop_[loop].e];
+ if (e->v1 == v) {
+ return e->v2;
+ }
+ return e->v1;
+ }
+
+ protected:
+ Mesh *mesh_;
+ MLoop *mloop_;
+ MPoly *mpoly_;
+ MEdge *medge_;
+ Vector<int> loop_to_poly_map_;
+};
+
+class BMeshFairingContext : public FairingContext {
+ public:
+ BMeshFairingContext(BMesh *bm)
+ {
+ this->bm = bm;
+ totvert_ = bm->totvert;
+ totloop_ = bm->totloop;
+
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ /* Deformation coords. */
+ co_.reserve(bm->totvert);
+ for (int i = 0; i < bm->totvert; i++) {
+ BMVert *v = BM_vert_at_index(bm, i);
+ co_[i] = v->co;
+ }
+
+ bmloop_.reserve(bm->totloop);
+ vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map");
+ vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool");
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter iter;
+ BMIter loop_iter;
+ int index_iter = 0;
+
+ /* This initializes both the bmloop and the vlmap for bmesh in a single loop. */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ int loop_count = 0;
+ const int vert_index = BM_elem_index_get(v);
+ vlmap_[vert_index].indices = &vlmap_mem_[index_iter];
+ BM_ITER_ELEM (l, &loop_iter, v, BM_LOOPS_OF_VERT) {
+ const int loop_index = BM_elem_index_get(l);
+ bmloop_[loop_index] = l;
+ vlmap_mem_[index_iter] = loop_index;
+ index_iter++;
+ loop_count++;
+ }
+ vlmap_[vert_index].count = loop_count;
+ }
+ }
+
+ ~BMeshFairingContext()
+ {
+ MEM_SAFE_FREE(vlmap_);
+ MEM_SAFE_FREE(vlmap_mem_);
+ }
+
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) override
+ {
+ copy_v3_v3(r_adj_next, bmloop_[loop]->next->v->co);
+ copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co);
+ }
+
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override
+ {
+ BMLoop *l = bmloop_[loop];
+ BMVert *bmvert = BM_vert_at_index(bm, v);
+ BMVert *bm_other_vert = BM_edge_other_vert(l->e, bmvert);
+ return BM_elem_index_get(bm_other_vert);
+ }
+
+ protected:
+ BMesh *bm;
+ Vector<BMLoop *> bmloop_;
+};
+
+class UniformVertexWeight : public VertexWeight {
+ public:
+ UniformVertexWeight(FairingContext *fairing_context)
+ {
+ const int totvert = fairing_context->vertex_count_get();
+ vertex_weights_.reserve(totvert);
+ for (int i = 0; i < totvert; i++) {
+ const int tot_loop = fairing_context->vertex_loop_map_get(i)->count;
+ if (tot_loop != 0) {
+ vertex_weights_[i] = 1.0f / tot_loop;
+ }
+ else {
+ vertex_weights_[i] = FLT_MAX;
+ }
+ }
+ }
+ ~UniformVertexWeight() = default;
+
+ float weight_at_index(const int index) override
+ {
+ return vertex_weights_[index];
+ }
+
+ private:
+ Vector<float> vertex_weights_;
+};
+
+class VoronoiVertexWeight : public VertexWeight {
+
+ public:
+ VoronoiVertexWeight(FairingContext *fairing_context)
+ {
+
+ const int totvert = fairing_context->vertex_count_get();
+ vertex_weights_.reserve(totvert);
+ for (int i = 0; i < totvert; i++) {
+
+ float area = 0.0f;
+ float a[3];
+ copy_v3_v3(a, fairing_context->vertex_deformation_co_get(i));
+ const float acute_threshold = M_PI_2;
+
+ MeshElemMap *vlmap_elem = fairing_context->vertex_loop_map_get(i);
+ for (int l = 0; l < vlmap_elem->count; l++) {
+ const int l_index = vlmap_elem->indices[l];
+
+ float b[3], c[3], d[3];
+ fairing_context->adjacents_coords_from_loop(l_index, b, c);
+
+ if (angle_v3v3v3(c, fairing_context->vertex_deformation_co_get(i), b) < acute_threshold) {
+ calc_circumcenter(d, a, b, c);
+ }
+ else {
+ add_v3_v3v3(d, b, c);
+ mul_v3_fl(d, 0.5f);
+ }
+
+ float t[3];
+ add_v3_v3v3(t, a, b);
+ mul_v3_fl(t, 0.5f);
+ area += area_tri_v3(a, t, d);
+
+ add_v3_v3v3(t, a, c);
+ mul_v3_fl(t, 0.5f);
+ area += area_tri_v3(a, d, t);
+ }
+
+ vertex_weights_[i] = area != 0.0f ? 1.0f / area : 1e12;
+ }
+ }
+ ~VoronoiVertexWeight() = default;
+
+ float weight_at_index(const int index) override
+ {
+ return vertex_weights_[index];
+ }
+
+ private:
+ Vector<float> vertex_weights_;
+
+ void calc_circumcenter(float r[3], const float a[3], const float b[3], const float c[3])
+ {
+ float ab[3];
+ sub_v3_v3v3(ab, b, a);
+
+ float ac[3];
+ sub_v3_v3v3(ac, c, a);
+
+ float ab_cross_ac[3];
+ cross_v3_v3v3(ab_cross_ac, ab, ac);
+
+ if (len_squared_v3(ab_cross_ac) > 0.0f) {
+ float d[3];
+ cross_v3_v3v3(d, ab_cross_ac, ab);
+ mul_v3_fl(d, len_squared_v3(ac));
+
+ float t[3];
+ cross_v3_v3v3(t, ac, ab_cross_ac);
+ mul_v3_fl(t, len_squared_v3(ab));
+
+ add_v3_v3(d, t);
+
+ mul_v3_fl(d, 1.0f / (2.0f * len_squared_v3(ab_cross_ac)));
+
+ add_v3_v3v3(r, a, d);
+ return;
+ }
+ copy_v3_v3(r, a);
+ }
+};
+
+class UniformLoopWeight : public LoopWeight {
+ public:
+ float weight_at_index(const int UNUSED(index)) override
+ {
+ return 1.0f;
+ }
+};
+
+static void prefair_and_fair_vertices(FairingContext *fairing_context,
+ bool *affected_vertices,
+ const eMeshFairingDepth depth)
+{
+ /* Prefair. */
+ UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context);
+ UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight();
+ fairing_context->fair_vertices(
+ affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights);
+ delete uniform_vertex_weights;
+
+ /* Fair. */
+ VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context);
+ /* TODO: Implemente cotangent loop weights. */
+ fairing_context->fair_vertices(
+ affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights);
+
+ delete uniform_loop_weights;
+ delete voronoi_vertex_weights;
+}
+
+void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth)
+{
+ MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts);
+ prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ delete fairing_context;
+}
+
+void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth)
+{
+ BMeshFairingContext *fairing_context = new BMeshFairingContext(bm);
+ prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ delete fairing_context;
+}
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 3496e05c6e5..ba0f59f6363 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -38,6 +38,7 @@
#include "DNA_armature_types.h"
#include "DNA_cloth_types.h"
+#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_mesh_types.h"
@@ -578,7 +579,7 @@ bool BKE_modifier_is_enabled(const struct Scene *scene, ModifierData *md, int re
* Check whether given modifier is not local (i.e. from linked data) when the object is a library
* override.
*
- * \param md May be NULL, in which case we consider it as a non-local modifier case.
+ * \param md: May be NULL, in which case we consider it as a non-local modifier case.
*/
bool BKE_modifier_is_nonlocal_in_liboverride(const Object *ob, const ModifierData *md)
{
diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.h b/source/blender/blenkernel/intern/multires_unsubdivide.h
index 39c6da0b6c8..0a03387282f 100644
--- a/source/blender/blenkernel/intern/multires_unsubdivide.h
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.h
@@ -26,10 +26,8 @@
#include "BLI_sys_types.h"
struct BMesh;
-struct Depsgraph;
struct Mesh;
struct MultiresModifierData;
-struct Object;
typedef struct MultiresUnsubdivideGrid {
/* For sanity checks. */
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index ebd9317fcf1..75230f9045c 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -334,11 +334,8 @@ NlaStrip *BKE_nlastrip_new(bAction *act)
/* generic settings
* - selected flag to highlight this to the user
* - (XXX) disabled Auto-Blends, as this was often causing some unwanted effects
- * - (XXX) synchronization of strip-length in accordance with changes to action-length
- * is not done though, since this should only really happens in editmode for strips now
- * though this decision is still subject to further review...
*/
- strip->flag = NLASTRIP_FLAG_SELECT;
+ strip->flag = NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_SYNC_LENGTH;
/* assign the action reference */
strip->act = act;
@@ -1195,7 +1192,7 @@ bool BKE_nlatrack_get_bounds(NlaTrack *nlt, float bounds[2])
* Check whether given NLA track is not local (i.e. from linked data) when the object is a library
* override.
*
- * \param nlt May be NULL, in which case we consider it as a non-local track case.
+ * \param nlt: May be NULL, in which case we consider it as a non-local track case.
*/
bool BKE_nlatrack_is_nonlocal_in_liboverride(const ID *id, const NlaTrack *nlt)
{
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 31de95817fd..415eb14be66 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -35,6 +35,7 @@
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
+#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
@@ -59,6 +60,7 @@
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
#include "BKE_colortools.h"
+#include "BKE_cryptomatte.h"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
@@ -274,6 +276,11 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *default_value = sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -373,6 +380,9 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_IMAGE:
BLO_write_struct(writer, bNodeSocketValueImage, sock->default_value);
break;
+ case SOCK_COLLECTION:
+ BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -482,10 +492,18 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_CRYPTOMATTE)) {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
+ /* Update the matte_id so the files can be opened in versions that don't
+ * use `CryptomatteEntry`. */
+ MEM_SAFE_FREE(nc->matte_id);
+ nc->matte_id = BKE_cryptomatte_entries_to_matte_id(nc);
if (nc->matte_id) {
BLO_write_string(writer, nc->matte_id);
}
+ LISTBASE_FOREACH (CryptomatteEntry *, entry, &nc->entries) {
+ BLO_write_struct(writer, CryptomatteEntry, entry);
+ }
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
+ MEM_SAFE_FREE(nc->matte_id);
}
else if (node->typeinfo != &NodeTypeUndefined) {
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
@@ -637,6 +655,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
case CMP_NODE_CRYPTOMATTE: {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
BLO_read_data_address(reader, &nc->matte_id);
+ BLO_read_list(reader, &nc->entries);
break;
}
case TEX_NODE_IMAGE: {
@@ -709,6 +728,11 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
BLO_read_id_address(reader, lib, &default_value->value);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueImage *default_value = sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -788,6 +812,11 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, default_value->value);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *default_value = sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1345,6 +1374,11 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus((ID *)default_value->value);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *default_value = sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1372,6 +1406,11 @@ static void socket_id_user_decrement(bNodeSocket *sock)
id_us_min(&default_value->value->id);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *default_value = sock->default_value;
+ id_us_min(&default_value->value->id);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1511,6 +1550,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketImage";
case SOCK_GEOMETRY:
return "NodeSocketGeometry";
+ case SOCK_COLLECTION:
+ return "NodeSocketCollection";
}
return NULL;
}
@@ -1578,6 +1619,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceImage";
case SOCK_GEOMETRY:
return "NodeSocketInterfaceGeometry";
+ case SOCK_COLLECTION:
+ return "NodeSocketInterfaceCollection";
}
return NULL;
}
@@ -3960,9 +4003,9 @@ void ntreeUpdateAllNew(Main *main)
FOREACH_NODETREE_END;
}
-void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
+void ntreeUpdateAllUsers(Main *main, ID *id)
{
- if (ngroup == NULL) {
+ if (id == NULL) {
return;
}
@@ -3971,7 +4014,7 @@ void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
bool need_update = false;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->id == &ngroup->id) {
+ if (node->id == id) {
if (node->typeinfo->group_update_func) {
node->typeinfo->group_update_func(ntree, node);
}
@@ -3986,13 +4029,16 @@ void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup)
}
FOREACH_NODETREE_END;
- if (ngroup->type == NTREE_GEOMETRY) {
- LISTBASE_FOREACH (Object *, object, &main->objects) {
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- if (nmd->node_group == ngroup) {
- MOD_nodes_update_interface(object, nmd);
+ if (GS(id->name) == ID_NT) {
+ bNodeTree *ngroup = (bNodeTree *)id;
+ if (ngroup->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (Object *, object, &main->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ if (nmd->node_group == ngroup) {
+ MOD_nodes_update_interface(object, nmd);
+ }
}
}
}
@@ -4041,7 +4087,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
}
if (bmain) {
- ntreeUpdateAllUsers(bmain, ntree);
+ ntreeUpdateAllUsers(bmain, &ntree->id);
}
if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) {
@@ -4682,6 +4728,7 @@ static void registerGeometryNodes(void)
{
register_node_type_geo_group();
+ register_node_type_geo_attribute_fill();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();
@@ -4690,9 +4737,11 @@ static void registerGeometryNodes(void)
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
register_node_type_geo_object_info();
- register_node_type_geo_random_attribute();
+ register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_math();
register_node_type_geo_join_geometry();
+ register_node_type_geo_attribute_mix();
+ register_node_type_geo_attribute_color_ramp();
}
static void registerFunctionNodes(void)
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index d747da2213e..8764232e56b 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -38,6 +38,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_defaults.h"
+#include "DNA_dynamicpaint_types.h"
#include "DNA_effect_types.h"
#include "DNA_fluid_types.h"
#include "DNA_gpencil_modifier_types.h"
@@ -1325,7 +1326,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
if (ob->type == OB_POINTCLOUD) {
- return (mti->modifyPointCloud != NULL);
+ return (mti->modifyGeometrySet != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
@@ -2208,7 +2209,9 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
BLI_listbase_clear(&psysn->childcachebufs);
if (flag & LIB_ID_CREATE_NO_MAIN) {
- BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
+ /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview
+ * creation. */
+ // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
psysn->flag |= PSYS_SHARED_CACHES;
BLI_assert(psysn->pointcache != NULL);
}
@@ -5217,8 +5220,11 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md)
AnimData *adt = ob->adt;
FCurve *fcu;
- char pattern[MAX_NAME + 16];
- BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md->name);
+ char md_name_esc[sizeof(md->name) * 2];
+ BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc));
+
+ char pattern[sizeof(md_name_esc) + 16];
+ BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md_name_esc);
/* action - check for F-Curves with paths containing 'modifiers[' */
if (adt->action) {
@@ -5260,8 +5266,11 @@ bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md)
AnimData *adt = ob->adt;
FCurve *fcu;
- char pattern[MAX_NAME + 32];
- BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md->name);
+ char md_name_esc[sizeof(md->name) * 2];
+ BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc));
+
+ char pattern[sizeof(md_name_esc) + 32];
+ BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md_name_esc);
/* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */
if (adt->action) {
@@ -5295,8 +5304,11 @@ bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx)
AnimData *adt = ob->adt;
FCurve *fcu;
- char pattern[MAX_NAME + 32];
- BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx->name);
+ char fx_name_esc[sizeof(fx->name) * 2];
+ BLI_str_escape(fx_name_esc, fx->name, sizeof(fx_name_esc));
+
+ char pattern[sizeof(fx_name_esc) + 32];
+ BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx_name_esc);
/* action - check for F-Curves with paths containing string[' */
if (adt->action) {
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index d5434710e23..8fa708f31cb 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -816,15 +816,13 @@ static void make_duplis_instances_component(const DupliContext *ctx)
float(*positions)[3];
float(*rotations)[3];
float(*scales)[3];
- Object **objects;
+ InstancedData *instanced_data;
const int amount = BKE_geometry_set_instances(
- ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects);
+ ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &instanced_data);
for (int i = 0; i < amount; i++) {
- Object *object = objects[i];
- if (object == NULL) {
- continue;
- }
+ InstancedData *data = &instanced_data[i];
+
float scale_matrix[4][4];
size_to_mat4(scale_matrix, scales[i]);
float rotation_matrix[4][4];
@@ -833,14 +831,43 @@ static void make_duplis_instances_component(const DupliContext *ctx)
mul_m4_m4m4(instance_offset_matrix, rotation_matrix, scale_matrix);
copy_v3_v3(instance_offset_matrix[3], positions[i]);
- float matrix[4][4];
- mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix);
- make_dupli(ctx, object, matrix, i);
+ if (data->type == INSTANCE_DATA_TYPE_OBJECT) {
+ Object *object = data->data.object;
+ if (object != NULL) {
+ float matrix[4][4];
+ mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix);
+ make_dupli(ctx, object, matrix, i);
+
+ float space_matrix[4][4];
+ mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat);
+ mul_m4_m4_pre(space_matrix, ctx->object->obmat);
+ make_recursive_duplis(ctx, object, space_matrix, i);
+ }
+ }
+ else if (data->type == INSTANCE_DATA_TYPE_COLLECTION) {
+ Collection *collection = data->data.collection;
+ if (collection != NULL) {
+ float collection_matrix[4][4];
+ unit_m4(collection_matrix);
+ sub_v3_v3(collection_matrix[3], collection->instance_offset);
+ mul_m4_m4_pre(collection_matrix, instance_offset_matrix);
+ mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
+
+ eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
+ if (object == ctx->object) {
+ continue;
+ }
+
+ float instance_matrix[4][4];
+ mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat);
- float space_matrix[4][4];
- mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat);
- mul_m4_m4_pre(space_matrix, ctx->object->obmat);
- make_recursive_duplis(ctx, object, space_matrix, i);
+ make_dupli(ctx, object, instance_matrix, i);
+ make_recursive_duplis(ctx, object, collection_matrix, i);
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ }
+ }
}
}
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 915ee56e2f7..1b4dc98252a 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -28,6 +28,7 @@
#include "DNA_key_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index d516de535f9..dce45f44583 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -41,6 +41,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
index 98a55c3de95..6e0965650d3 100644
--- a/source/blender/blenkernel/intern/particle_child.c
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -25,6 +25,7 @@
#include "BLI_noise.h"
#include "DNA_material_types.h"
+#include "DNA_object_types.h"
#include "BKE_colortools.h"
#include "BKE_particle.h"
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index 194593be4ff..c3cc9136057 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -40,6 +40,7 @@
#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
+#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 35265cf8b68..71df28c8b42 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -34,6 +34,7 @@
#include "DNA_anim_types.h"
#include "DNA_boid_types.h"
+#include "DNA_cloth_types.h"
#include "DNA_curve_types.h"
#include "DNA_listBase.h"
#include "DNA_mesh_types.h"
@@ -4978,6 +4979,24 @@ void particle_system_update(struct Depsgraph *depsgraph,
/* ID looper */
+/* unfortunately PSys and modifier ID loopers are not directly compatible, so we need this struct
+ * and the callback below to map the former to the latter (thanks to psys embedding a Cloth
+ * modifier data struct now, for Hair physics simulations). */
+typedef struct ParticleSystemIDLoopForModifier {
+ ParticleSystem *psys;
+ ParticleSystemIDFunc func;
+ void *userdata;
+} ParticleSystemIDLoopForModifier;
+
+static void particlesystem_modifiersForeachIDLink(void *user_data,
+ Object *UNUSED(object),
+ ID **id_pointer,
+ int cb_flag)
+{
+ ParticleSystemIDLoopForModifier *data = (ParticleSystemIDLoopForModifier *)user_data;
+ data->func(data->psys, id_pointer, data->userdata, cb_flag);
+}
+
void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata)
{
ParticleTarget *pt;
@@ -4986,6 +5005,16 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func,
func(psys, (ID **)&psys->target_ob, userdata, IDWALK_CB_NOP);
func(psys, (ID **)&psys->parent, userdata, IDWALK_CB_NOP);
+ if (psys->clmd != NULL) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(psys->clmd->modifier.type);
+
+ if (mti->foreachIDLink != NULL) {
+ ParticleSystemIDLoopForModifier data = {.psys = psys, .func = func, .userdata = userdata};
+ mti->foreachIDLink(
+ &psys->clmd->modifier, NULL, particlesystem_modifiersForeachIDLink, &data);
+ }
+ }
+
for (pt = psys->targets.first; pt; pt = pt->next) {
func(psys, (ID **)&pt->ob, userdata, IDWALK_CB_NOP);
}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index 5f6685817b9..7bd14e80333 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -365,12 +365,31 @@ static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if (mti->modifyPointCloud) {
- mti->modifyPointCloud(md, &mectx, &geometry_set);
+ if (mti->modifyGeometrySet) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
}
+static PointCloud *take_pointcloud_ownership_from_geometry_set(GeometrySet &geometry_set)
+{
+ if (!geometry_set.has<PointCloudComponent>()) {
+ return nullptr;
+ }
+ PointCloudComponent &pointcloud_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+ PointCloud *pointcloud = pointcloud_component.release();
+ if (pointcloud != nullptr) {
+ /* Add back, but as read-only non-owning component. */
+ pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
+ }
+ else {
+ /* The component was empty, we can also remove it. */
+ geometry_set.remove<PointCloudComponent>();
+ }
+ return pointcloud;
+}
+
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -382,10 +401,17 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene
GeometryOwnershipType::ReadOnly);
pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
+ PointCloud *pointcloud_eval = take_pointcloud_ownership_from_geometry_set(geometry_set);
+
+ /* If the geometry set did not contain a point cloud, we still create an empty one. */
+ if (pointcloud_eval == nullptr) {
+ pointcloud_eval = BKE_pointcloud_new_nomain(0);
+ }
+
/* Assign evaluated object. */
- PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0);
- BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true);
- object->runtime.geometry_set_eval = new GeometrySet(geometry_set);
+ const bool eval_is_owned = pointcloud_eval != pointcloud;
+ BKE_object_eval_assign_data(object, &pointcloud_eval->id, eval_is_owned);
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
/* Draw Cache */
diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c
new file mode 100644
index 00000000000..8dcf6de164a
--- /dev/null
+++ b/source/blender/blenkernel/intern/preferences.c
@@ -0,0 +1,119 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * User defined asset library API.
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_string_utf8.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_appdir.h"
+#include "BKE_preferences.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_userdef_types.h"
+
+#define U BLI_STATIC_ASSERT(false, "Global 'U' not allowed, only use arguments passed in!")
+
+/* -------------------------------------------------------------------- */
+/** \name Asset Libraries
+ * \{ */
+
+bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef,
+ const char *name,
+ const char *path)
+{
+ bUserAssetLibrary *library = MEM_callocN(sizeof(*library), "bUserAssetLibrary");
+
+ BLI_addtail(&userdef->asset_libraries, library);
+
+ if (name) {
+ BKE_preferences_asset_library_name_set(userdef, library, name);
+ }
+ if (path) {
+ BLI_strncpy(library->path, path, sizeof(library->path));
+ }
+
+ return library;
+}
+
+void BKE_preferences_asset_library_name_set(UserDef *userdef,
+ bUserAssetLibrary *library,
+ const char *name)
+{
+ BLI_strncpy_utf8(library->name, name, sizeof(library->name));
+ BLI_uniquename(&userdef->asset_libraries,
+ library,
+ name,
+ '.',
+ offsetof(bUserAssetLibrary, name),
+ sizeof(library->name));
+}
+
+/**
+ * Unlink and free a library preference member.
+ * \note Free's \a library itself.
+ */
+void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library)
+{
+ BLI_freelinkN(&userdef->asset_libraries, library);
+}
+
+bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(const UserDef *userdef, int index)
+{
+ return BLI_findlink(&userdef->asset_libraries, index);
+}
+
+bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(const UserDef *userdef,
+ const char *name)
+{
+ return BLI_findstring(&userdef->asset_libraries, name, offsetof(bUserAssetLibrary, name));
+}
+
+int BKE_preferences_asset_library_get_index(const UserDef *userdef,
+ const bUserAssetLibrary *library)
+{
+ return BLI_findindex(&userdef->asset_libraries, library);
+}
+
+void BKE_preferences_asset_library_default_add(UserDef *userdef)
+{
+ char documents_path[FILE_MAXDIR];
+
+ /* No home or documents path found, not much we can do. */
+ if (!BKE_appdir_folder_documents(documents_path) || !documents_path[0]) {
+ return;
+ }
+
+ bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL);
+
+ /* Add new "Default" library under '[doc_path]/Blender/Assets'. */
+ BLI_path_join(
+ library->path, sizeof(library->path), documents_path, N_("Blender"), N_("Assets"), NULL);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index bed10df5ace..cc192c1c3c0 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -37,6 +37,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_mask_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@@ -47,6 +48,7 @@
#include "DNA_sound_types.h"
#include "DNA_space_types.h"
#include "DNA_text_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
@@ -220,6 +222,7 @@ static void scene_init_data(ID *id)
/* Curve Profile */
scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
+ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
scene->orientation_slots[i].index_custom = -1;
@@ -860,6 +863,9 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
if (tos->custom_bevel_profile_preset) {
BKE_curveprofile_blend_write(writer, tos->custom_bevel_profile_preset);
}
+ if (tos->sequencer_tool_settings) {
+ BLO_write_struct(writer, SequencerToolSettings, tos->sequencer_tool_settings);
+ }
BKE_paint_blend_write(writer, &tos->imapaint.paint);
@@ -1119,6 +1125,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
if (sce->toolsettings->custom_bevel_profile_preset) {
BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset);
}
+
+ BLO_read_data_address(reader, &sce->toolsettings->sequencer_tool_settings);
}
if (sce->ed) {
@@ -1790,6 +1798,8 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
ts->gp_sculpt.cur_primitive = BKE_curvemapping_copy(ts->gp_sculpt.cur_primitive);
ts->custom_bevel_profile_preset = BKE_curveprofile_copy(ts->custom_bevel_profile_preset);
+
+ ts->sequencer_tool_settings = SEQ_tool_settings_copy(ts->sequencer_tool_settings);
return ts;
}
@@ -1848,6 +1858,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_curveprofile_free(toolsettings->custom_bevel_profile_preset);
}
+ if (toolsettings->sequencer_tool_settings) {
+ SEQ_tool_settings_free(toolsettings->sequencer_tool_settings);
+ }
+
MEM_freeN(toolsettings);
}
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 355db8f0d60..52c41c9fd05 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -34,6 +34,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_collection_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_types.h"
#include "DNA_mask_types.h"
@@ -1265,6 +1266,9 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area)
if (sfile->params) {
BLO_write_struct(writer, FileSelectParams, sfile->params);
}
+ if (sfile->asset_params) {
+ BLO_write_struct(writer, FileAssetSelectParams, sfile->asset_params);
+ }
}
else if (sl->spacetype == SPACE_SEQ) {
BLO_write_struct(writer, SpaceSeq, sl);
@@ -1662,11 +1666,14 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
* plus, it isn't saved to files yet!
*/
sfile->folders_prev = sfile->folders_next = NULL;
+ BLI_listbase_clear(&sfile->folder_histories);
sfile->files = NULL;
sfile->layout = NULL;
sfile->op = NULL;
sfile->previews_timer = NULL;
+ sfile->tags = 0;
BLO_read_data_address(reader, &sfile->params);
+ BLO_read_data_address(reader, &sfile->asset_params);
}
else if (sl->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)sl;
@@ -1750,8 +1757,11 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
}
break;
}
- case SPACE_FILE:
+ case SPACE_FILE: {
+ SpaceFile *sfile = (SpaceFile *)sl;
+ sfile->tags |= FILE_TAG_REBUILD_MAIN_FILES;
break;
+ }
case SPACE_ACTION: {
SpaceAction *saction = (SpaceAction *)sl;
bDopeSheet *ads = &saction->ads;
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 25d812884bc..736acd76dfd 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -52,6 +52,7 @@
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index 8b66b1fc628..5bcc1a9553a 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -135,13 +135,19 @@ static void sound_foreach_cache(ID *id,
static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bSound *sound = (bSound *)id;
- if (sound->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (sound->id.us > 0 || is_undo) {
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
sound->tags = 0;
sound->handle = NULL;
sound->playback_handle = NULL;
sound->spinlock = NULL;
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) {
+ sound->packedfile = NULL;
+ }
+
/* write LibData */
BLO_write_id_struct(writer, bSound, id_address, &sound->id);
BKE_id_blend_write(writer, &sound->id);
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 93795af7cd7..9ef2e818293 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -459,12 +459,14 @@ bool BKE_text_reload(Text *text)
return true;
}
-/** Load a text file.
+/**
+ * Load a text file.
*
- * \param is_internal If \a true, this text data-block only exists in memory, not as a file on
- * disk.
+ * \param is_internal: If \a true, this text data-block only exists in memory,
+ * not as a file on disk.
*
- * \note: text data-blocks have no user by default, only the 'real user' flag. */
+ * \note text data-blocks have no user by default, only the 'real user' flag.
+ */
Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
{
unsigned char *buffer;
@@ -520,7 +522,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
/** Load a text file.
*
- * \note: text data-blocks have no user by default, only the 'real user' flag. */
+ * \note Text data-blocks have no user by default, only the 'real user' flag.
+ */
Text *BKE_text_load(Main *bmain, const char *file, const char *relpath)
{
return BKE_text_load_ex(bmain, file, relpath, false);
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 11aa9597740..67727d87161 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -561,10 +561,16 @@ static void volume_foreach_cache(ID *id,
static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Volume *volume = (Volume *)id;
- if (volume->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (volume->id.us > 0 || is_undo) {
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
volume->runtime.grids = nullptr;
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) {
+ volume->packedfile = nullptr;
+ }
+
/* write LibData */
BLO_write_id_struct(writer, Volume, id_address, &volume->id);
BKE_id_blend_write(writer, &volume->id);
diff --git a/source/blender/blenkernel/particle_private.h b/source/blender/blenkernel/particle_private.h
index 33277d1caac..1c183aab3b7 100644
--- a/source/blender/blenkernel/particle_private.h
+++ b/source/blender/blenkernel/particle_private.h
@@ -27,6 +27,8 @@
extern "C" {
#endif
+struct CurveMapping;
+
typedef struct ParticleChildModifierContext {
ParticleThreadContext *thread_ctx;
ParticleSimulationData *sim;
@@ -62,7 +64,7 @@ float do_clump(ParticleKey *state,
float pa_clump,
bool use_clump_noise,
float clump_noise_size,
- CurveMapping *clumpcurve);
+ struct CurveMapping *clumpcurve);
void do_child_modifiers(const ParticleChildModifierContext *modifier_ctx,
float mat[4][4],
ParticleKey *state,
diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh
index 8a7dcb7ffaa..284d62fb876 100644
--- a/source/blender/blenlib/BLI_array.hh
+++ b/source/blender/blenlib/BLI_array.hh
@@ -220,13 +220,13 @@ class Array {
return MutableSpan<T>(data_, size_);
}
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr>
operator Span<U>() const
{
return Span<U>(data_, size_);
}
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr>
operator MutableSpan<U>()
{
return MutableSpan<U>(data_, size_);
diff --git a/source/blender/blenlib/BLI_hash.h b/source/blender/blenlib/BLI_hash.h
index c2be416ef5f..d687e805323 100644
--- a/source/blender/blenlib/BLI_hash.h
+++ b/source/blender/blenlib/BLI_hash.h
@@ -26,35 +26,59 @@
extern "C" {
#endif
-BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
-{
+/**
+ * Jenkins Lookup3 Hash Functions.
+ * Source: http://burtleburtle.net/bob/c/lookup3.c
+ */
+
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+#define final(a, b, c) \
+ { \
+ c ^= b; \
+ c -= rot(b, 14); \
+ a ^= c; \
+ a -= rot(c, 11); \
+ b ^= a; \
+ b -= rot(a, 25); \
+ c ^= b; \
+ c -= rot(b, 16); \
+ a ^= c; \
+ a -= rot(c, 4); \
+ b ^= a; \
+ b -= rot(a, 14); \
+ c ^= b; \
+ c -= rot(b, 24); \
+ } \
+ ((void)0)
+
+BLI_INLINE unsigned int BLI_hash_int_3d(unsigned int kx, unsigned int ky, unsigned int kz)
+{
+ unsigned int a, b, c;
+ a = b = c = 0xdeadbeef + (3 << 2) + 13;
+
+ c += kz;
+ b += ky;
+ a += kx;
+ final(a, b, c);
+
+ return c;
+}
+BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
+{
unsigned int a, b, c;
a = b = c = 0xdeadbeef + (2 << 2) + 13;
a += kx;
b += ky;
- c ^= b;
- c -= rot(b, 14);
- a ^= c;
- a -= rot(c, 11);
- b ^= a;
- b -= rot(a, 25);
- c ^= b;
- c -= rot(b, 16);
- a ^= c;
- a -= rot(c, 4);
- b ^= a;
- b -= rot(a, 14);
- c ^= b;
- c -= rot(b, 24);
+ final(a, b, c);
return c;
+}
+#undef final
#undef rot
-}
BLI_INLINE unsigned int BLI_hash_string(const char *str)
{
diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh
index 2b060c986cd..4121542c887 100644
--- a/source/blender/blenlib/BLI_index_range.hh
+++ b/source/blender/blenlib/BLI_index_range.hh
@@ -73,21 +73,22 @@ class IndexRange {
int64_t size_ = 0;
public:
- IndexRange() = default;
+ constexpr IndexRange() = default;
- explicit IndexRange(int64_t size) : start_(0), size_(size)
+ constexpr explicit IndexRange(int64_t size) : start_(0), size_(size)
{
BLI_assert(size >= 0);
}
- IndexRange(int64_t start, int64_t size) : start_(start), size_(size)
+ constexpr IndexRange(int64_t start, int64_t size) : start_(start), size_(size)
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
}
template<typename T>
- IndexRange(const tbb::blocked_range<T> &range) : start_(range.begin()), size_(range.size())
+ constexpr IndexRange(const tbb::blocked_range<T> &range)
+ : start_(range.begin()), size_(range.size())
{
}
@@ -96,33 +97,33 @@ class IndexRange {
int64_t current_;
public:
- Iterator(int64_t current) : current_(current)
+ constexpr Iterator(int64_t current) : current_(current)
{
}
- Iterator &operator++()
+ constexpr Iterator &operator++()
{
current_++;
return *this;
}
- bool operator!=(const Iterator &iterator) const
+ constexpr bool operator!=(const Iterator &iterator) const
{
return current_ != iterator.current_;
}
- int64_t operator*() const
+ constexpr int64_t operator*() const
{
return current_;
}
};
- Iterator begin() const
+ constexpr Iterator begin() const
{
return Iterator(start_);
}
- Iterator end() const
+ constexpr Iterator end() const
{
return Iterator(start_ + size_);
}
@@ -130,7 +131,7 @@ class IndexRange {
/**
* Access an element in the range.
*/
- int64_t operator[](int64_t index) const
+ constexpr int64_t operator[](int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < this->size());
@@ -140,7 +141,7 @@ class IndexRange {
/**
* Two ranges compare equal when they contain the same numbers.
*/
- friend bool operator==(IndexRange a, IndexRange b)
+ constexpr friend bool operator==(IndexRange a, IndexRange b)
{
return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0);
}
@@ -148,7 +149,7 @@ class IndexRange {
/**
* Get the amount of numbers in the range.
*/
- int64_t size() const
+ constexpr int64_t size() const
{
return size_;
}
@@ -156,7 +157,7 @@ class IndexRange {
/**
* Create a new range starting at the end of the current one.
*/
- IndexRange after(int64_t n) const
+ constexpr IndexRange after(int64_t n) const
{
BLI_assert(n >= 0);
return IndexRange(start_ + size_, n);
@@ -165,7 +166,7 @@ class IndexRange {
/**
* Create a new range that ends at the start of the current one.
*/
- IndexRange before(int64_t n) const
+ constexpr IndexRange before(int64_t n) const
{
BLI_assert(n >= 0);
return IndexRange(start_ - n, n);
@@ -175,7 +176,7 @@ class IndexRange {
* Get the first element in the range.
* Asserts when the range is empty.
*/
- int64_t first() const
+ constexpr int64_t first() const
{
BLI_assert(this->size() > 0);
return start_;
@@ -185,7 +186,7 @@ class IndexRange {
* Get the last element in the range.
* Asserts when the range is empty.
*/
- int64_t last() const
+ constexpr int64_t last() const
{
BLI_assert(this->size() > 0);
return start_ + size_ - 1;
@@ -194,7 +195,7 @@ class IndexRange {
/**
* Get the element one after the end. The returned value is undefined when the range is empty.
*/
- int64_t one_after_last() const
+ constexpr int64_t one_after_last() const
{
return start_ + size_;
}
@@ -202,7 +203,7 @@ class IndexRange {
/**
* Get the first element in the range. The returned value is undefined when the range is empty.
*/
- int64_t start() const
+ constexpr int64_t start() const
{
return start_;
}
@@ -210,7 +211,7 @@ class IndexRange {
/**
* Returns true when the range contains a certain number, otherwise false.
*/
- bool contains(int64_t value) const
+ constexpr bool contains(int64_t value) const
{
return value >= start_ && value < start_ + size_;
}
@@ -218,7 +219,7 @@ class IndexRange {
/**
* Returns a new range, that contains a sub-interval of the current one.
*/
- IndexRange slice(int64_t start, int64_t size) const
+ constexpr IndexRange slice(int64_t start, int64_t size) const
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
@@ -226,7 +227,7 @@ class IndexRange {
BLI_assert(new_start + size <= start_ + size_ || size == 0);
return IndexRange(new_start, size);
}
- IndexRange slice(IndexRange range) const
+ constexpr IndexRange slice(IndexRange range) const
{
return this->slice(range.start(), range.size());
}
diff --git a/source/blender/blenlib/BLI_inplace_priority_queue.hh b/source/blender/blenlib/BLI_inplace_priority_queue.hh
new file mode 100644
index 00000000000..e76cb8504a3
--- /dev/null
+++ b/source/blender/blenlib/BLI_inplace_priority_queue.hh
@@ -0,0 +1,304 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_array.hh"
+#include "BLI_dot_export.hh"
+
+namespace blender {
+
+/**
+ * An InplacePriorityQueue adds priority queue functionality to an existing array. The underlying
+ * array is not changed. Instead, the priority queue maintains indices into the original array.
+ *
+ * The priority queue provides efficient access to the element in order of their priorities.
+ *
+ * When a priority changes, the priority queue has to be informed using one of the following
+ * methods: #priority_decreased, #priority_increased or #priority_changed.
+ */
+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. */
+ typename FirstHasHigherPriority = std::greater<T>>
+class InplacePriorityQueue {
+ private:
+ /* Underlying array the priority queue is built upon. This is a span instead of a mutable span,
+ * because this data structure never changes the values itself. */
+ Span<T> data_;
+ /* Maps indices from the heap (binary tree in array format) to indices of the underlying/original
+ * array. */
+ Array<int64_t> heap_to_orig_;
+ /* This is the inversion of the above mapping. */
+ Array<int64_t> orig_to_heap_;
+ /* Number of elements that are currently in the priority queue. */
+ int64_t heap_size_ = 0;
+ /* Function that can be changed to customize how the priority of two elements is compared. */
+ FirstHasHigherPriority first_has_higher_priority_fn_;
+
+ public:
+ /**
+ * Construct the priority queue on top of the data in the given span.
+ */
+ InplacePriorityQueue(Span<T> data)
+ : data_(data), heap_to_orig_(data_.size()), orig_to_heap_(data_.size())
+ {
+ for (const int64_t i : IndexRange(data_.size())) {
+ heap_to_orig_[i] = i;
+ orig_to_heap_[i] = i;
+ }
+
+ this->rebuild();
+ }
+
+ /**
+ * Rebuilds the priority queue from the array that has been passed to the constructor.
+ */
+ void rebuild()
+ {
+ const int final_heap_size = data_.size();
+ if (final_heap_size > 1) {
+ for (int64_t i = this->get_parent(final_heap_size - 1); i >= 0; i--) {
+ this->heapify(i, final_heap_size);
+ }
+ }
+ heap_size_ = final_heap_size;
+ }
+
+ /**
+ * Returns the number of elements in the priority queue.
+ * This is less or equal than the size of the underlying array.
+ */
+ int64_t size() const
+ {
+ return heap_size_;
+ }
+
+ /**
+ * Returns true, when the priority queue contains no elements. If this returns true, #peek and
+ * #pop must not be used.
+ */
+ bool is_empty() const
+ {
+ return heap_size_ == 0;
+ }
+
+ /**
+ * Get the element with the highest priority in the priority queue.
+ * The returned reference is const, because the priority queue has read-only access to the
+ * underlying data. If you need a mutable reference, use #peek_index instead.
+ */
+ const T &peek() const
+ {
+ return data_[this->peek_index()];
+ }
+
+ /**
+ * Get the element with the highest priority in the priority queue and remove it.
+ * The returned reference is const, because the priority queue has read-only access to the
+ * underlying data. If you need a mutable reference, use #pop_index instead.
+ */
+ const T &pop()
+ {
+ return data_[this->pop_index()];
+ }
+
+ /**
+ * Get the index of the element with the highest priority in the priority queue.
+ */
+ int64_t peek_index() const
+ {
+ BLI_assert(!this->is_empty());
+ return heap_to_orig_[0];
+ }
+
+ /**
+ * Get the index of the element with the highest priority in the priority queue and remove it.
+ */
+ int64_t pop_index()
+ {
+ BLI_assert(!this->is_empty());
+ const int64_t top_index_orig = heap_to_orig_[0];
+ heap_size_--;
+ if (heap_size_ > 1) {
+ this->swap_indices(0, heap_size_);
+ this->heapify(0, heap_size_);
+ }
+ return top_index_orig;
+ }
+
+ /**
+ * Inform the priority queue that the priority of the element at the given index has been
+ * decreased.
+ */
+ void priority_decreased(const int64_t index)
+ {
+ const int64_t heap_index = orig_to_heap_[index];
+ if (heap_index >= heap_size_) {
+ /* This element is not in the queue currently. */
+ return;
+ }
+ this->heapify(heap_index, heap_size_);
+ }
+
+ /**
+ * Inform the priority queue that the priority of the element at the given index has been
+ * increased.
+ */
+ void priority_increased(const int64_t index)
+ {
+ int64_t current = orig_to_heap_[index];
+ if (current >= heap_size_) {
+ /* This element is not in the queue currently. */
+ return;
+ }
+ while (true) {
+ if (current == 0) {
+ break;
+ }
+ const int64_t parent = this->get_parent(current);
+ if (this->first_has_higher_priority(parent, current)) {
+ break;
+ }
+ this->swap_indices(current, parent);
+ current = parent;
+ }
+ }
+
+ /**
+ * Inform the priority queue that the priority of the element at the given index has been
+ * changed.
+ */
+ void priority_changed(const int64_t index)
+ {
+ this->priority_increased(index);
+ this->priority_decreased(index);
+ }
+
+ /**
+ * Returns the indices of all elements that are in the priority queue.
+ * There are no guarantees about the order of indices.
+ */
+ Span<int64_t> active_indices() const
+ {
+ return heap_to_orig_.as_span().take_front(heap_size_);
+ }
+
+ /**
+ * Returns the indices of all elements that are not in the priority queue.
+ * The indices are in reverse order of their removal from the queue.
+ * I.e. the index that has been removed last, comes first.
+ */
+ Span<int64_t> inactive_indices() const
+ {
+ return heap_to_orig_.as_span().drop_front(heap_size_);
+ }
+
+ /**
+ * Returns the concatenation of the active and inactive indices.
+ */
+ Span<int64_t> all_indices() const
+ {
+ return heap_to_orig_;
+ }
+
+ /**
+ * Return the heap used by the priority queue as dot graph string.
+ * This exists for debugging purposes.
+ */
+ std::string to_dot() const
+ {
+ return this->partial_to_dot(heap_size_);
+ }
+
+ private:
+ bool first_has_higher_priority(const int64_t a, const int64_t b)
+ {
+ const T &value_a = data_[heap_to_orig_[a]];
+ const T &value_b = data_[heap_to_orig_[b]];
+ return first_has_higher_priority_fn_(value_a, value_b);
+ }
+
+ void swap_indices(const int64_t a, const int64_t b)
+ {
+ std::swap(heap_to_orig_[a], heap_to_orig_[b]);
+ orig_to_heap_[heap_to_orig_[a]] = a;
+ orig_to_heap_[heap_to_orig_[b]] = b;
+ }
+
+ void heapify(const int64_t parent, const int64_t heap_size)
+ {
+ int64_t max_index = parent;
+ const int left = this->get_left(parent);
+ const int right = this->get_right(parent);
+ if (left < heap_size && this->first_has_higher_priority(left, max_index)) {
+ max_index = left;
+ }
+ if (right < heap_size && this->first_has_higher_priority(right, max_index)) {
+ max_index = right;
+ }
+ if (max_index != parent) {
+ this->swap_indices(parent, max_index);
+ this->heapify(max_index, heap_size);
+ }
+ if (left < heap_size) {
+ BLI_assert(!this->first_has_higher_priority(left, parent));
+ }
+ if (right < heap_size) {
+ BLI_assert(!this->first_has_higher_priority(right, parent));
+ }
+ }
+
+ int64_t get_parent(const int64_t child) const
+ {
+ BLI_assert(child > 0);
+ return (child - 1) / 2;
+ }
+
+ int64_t get_left(const int64_t parent) const
+ {
+ return parent * 2 + 1;
+ }
+
+ int64_t get_right(const int64_t parent) const
+ {
+ return parent * 2 + 2;
+ }
+
+ std::string partial_to_dot(const int size) const
+ {
+ dot::DirectedGraph digraph;
+ Array<dot::Node *> dot_nodes(size);
+ for (const int i : IndexRange(size)) {
+ std::stringstream ss;
+ ss << data_[heap_to_orig_[i]];
+ const std::string name = ss.str();
+ dot::Node &node = digraph.new_node(name);
+ node.set_shape(dot::Attr_shape::Rectangle);
+ node.attributes.set("ordering", "out");
+ dot_nodes[i] = &node;
+ if (i > 0) {
+ const int64_t parent = this->get_parent(i);
+ digraph.new_edge(*dot_nodes[parent], node);
+ }
+ }
+ return digraph.to_dot_string();
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h
index c34b71a60f9..5e0ea4f2a99 100644
--- a/source/blender/blenlib/BLI_kdopbvh.h
+++ b/source/blender/blenlib/BLI_kdopbvh.h
@@ -178,6 +178,7 @@ int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersec
int BLI_bvhtree_get_len(const BVHTree *tree);
int BLI_bvhtree_get_tree_type(const BVHTree *tree);
float BLI_bvhtree_get_epsilon(const BVHTree *tree);
+void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]);
/* find nearest node to the given coordinates
* (if nearest is given it will only search nodes where
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index 49076bb1aae..b3b6855089e 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -428,6 +428,25 @@ inline constexpr bool is_convertible_pointer_v =
std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>;
/**
+ * Helper variable that checks if a Span<From> can be converted to Span<To> safely, whereby From
+ * and To are pointers. Adding const and casting to a void pointer is allowed.
+ * Casting up and down a class hierarchy generally is not allowed, because this might change the
+ * pointer under some circumstances.
+ */
+template<typename From, typename To>
+inline constexpr bool is_span_convertible_pointer_v =
+ /* Make sure we are working with pointers. */
+ std::is_pointer_v<From> &&std::is_pointer_v<To> &&
+ (/* No casting is necessary when both types are the same. */
+ std::is_same_v<From, To> ||
+ /* Allow adding const to the underlying type. */
+ std::is_same_v<const std::remove_pointer_t<From>, std::remove_pointer_t<To>> ||
+ /* Allow casting non-const pointers to void pointers. */
+ (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) ||
+ /* Allow casting any pointer to const void pointers. */
+ std::is_same_v<To, const void *>);
+
+/**
* Inline buffers for small-object-optimization should be disable by default. Otherwise we might
* get large unexpected allocations on the stack.
*/
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index 5b4d2769f57..8011b2f9abc 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -93,15 +93,15 @@ template<typename T> class Span {
/**
* Create a reference to an empty array.
*/
- Span() = default;
+ constexpr Span() = default;
- Span(const T *start, int64_t size) : data_(start), size_(size)
+ constexpr Span(const T *start, int64_t size) : data_(start), size_(size)
{
BLI_assert(size >= 0);
}
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
- Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size)
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr>
+ constexpr Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size)
{
BLI_assert(size >= 0);
}
@@ -117,16 +117,17 @@ template<typename T> class Span {
* Span<int> span = {1, 2, 3, 4};
* call_function_with_array(span);
*/
- Span(const std::initializer_list<T> &list)
+ constexpr Span(const std::initializer_list<T> &list)
: Span(list.begin(), static_cast<int64_t>(list.size()))
{
}
- Span(const std::vector<T> &vector) : Span(vector.data(), static_cast<int64_t>(vector.size()))
+ constexpr Span(const std::vector<T> &vector)
+ : Span(vector.data(), static_cast<int64_t>(vector.size()))
{
}
- template<std::size_t N> Span(const std::array<T, N> &array) : Span(array.data(), N)
+ template<std::size_t N> constexpr Span(const std::array<T, N> &array) : Span(array.data(), N)
{
}
@@ -134,8 +135,9 @@ template<typename T> class Span {
* Support implicit conversions like the ones below:
* Span<T *> -> Span<const T *>
*/
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr>
- Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size())
+
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr>
+ constexpr Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size())
{
}
@@ -143,7 +145,7 @@ template<typename T> class Span {
* Returns a contiguous part of the array. This invokes undefined behavior when the slice does
* not stay within the bounds of the array.
*/
- Span slice(int64_t start, int64_t size) const
+ constexpr Span slice(int64_t start, int64_t size) const
{
BLI_assert(start >= 0);
BLI_assert(size >= 0);
@@ -151,7 +153,7 @@ template<typename T> class Span {
return Span(data_ + start, size);
}
- Span slice(IndexRange range) const
+ constexpr Span slice(IndexRange range) const
{
return this->slice(range.start(), range.size());
}
@@ -160,7 +162,7 @@ template<typename T> class Span {
* Returns a new Span with n elements removed from the beginning. This invokes undefined
* behavior when the array is too small.
*/
- Span drop_front(int64_t n) const
+ constexpr Span drop_front(int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= this->size());
@@ -171,7 +173,7 @@ template<typename T> class Span {
* Returns a new Span with n elements removed from the beginning. This invokes undefined
* behavior when the array is too small.
*/
- Span drop_back(int64_t n) const
+ constexpr Span drop_back(int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= this->size());
@@ -182,7 +184,7 @@ template<typename T> class Span {
* Returns a new Span that only contains the first n elements. This invokes undefined
* behavior when the array is too small.
*/
- Span take_front(int64_t n) const
+ constexpr Span take_front(int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= this->size());
@@ -193,7 +195,7 @@ template<typename T> class Span {
* Returns a new Span that only contains the last n elements. This invokes undefined
* behavior when the array is too small.
*/
- Span take_back(int64_t n) const
+ constexpr Span take_back(int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= this->size());
@@ -204,25 +206,25 @@ template<typename T> class Span {
* Returns the pointer to the beginning of the referenced array. This may be nullptr when the
* size is zero.
*/
- const T *data() const
+ constexpr const T *data() const
{
return data_;
}
- const T *begin() const
+ constexpr const T *begin() const
{
return data_;
}
- const T *end() const
+ constexpr const T *end() const
{
return data_ + size_;
}
- std::reverse_iterator<const T *> rbegin() const
+ constexpr std::reverse_iterator<const T *> rbegin() const
{
return std::reverse_iterator<const T *>(this->end());
}
- std::reverse_iterator<const T *> rend() const
+ constexpr std::reverse_iterator<const T *> rend() const
{
return std::reverse_iterator<const T *>(this->begin());
}
@@ -231,7 +233,7 @@ template<typename T> class Span {
* Access an element in the array. This invokes undefined behavior when the index is out of
* bounds.
*/
- const T &operator[](int64_t index) const
+ constexpr const T &operator[](int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < size_);
@@ -241,7 +243,7 @@ template<typename T> class Span {
/**
* Returns the number of elements in the referenced array.
*/
- int64_t size() const
+ constexpr int64_t size() const
{
return size_;
}
@@ -249,7 +251,7 @@ template<typename T> class Span {
/**
* Returns true if the size is zero.
*/
- bool is_empty() const
+ constexpr bool is_empty() const
{
return size_ == 0;
}
@@ -257,7 +259,7 @@ template<typename T> class Span {
/**
* Returns the number of bytes referenced by this Span.
*/
- int64_t size_in_bytes() const
+ constexpr int64_t size_in_bytes() const
{
return sizeof(T) * size_;
}
@@ -266,7 +268,7 @@ template<typename T> class Span {
* Does a linear search to see of the value is in the array.
* Returns true if it is, otherwise false.
*/
- bool contains(const T &value) const
+ constexpr bool contains(const T &value) const
{
for (const T &element : *this) {
if (element == value) {
@@ -280,7 +282,7 @@ template<typename T> class Span {
* Does a constant time check to see if the pointer points to a value in the referenced array.
* Return true if it is, otherwise false.
*/
- bool contains_ptr(const T *ptr) const
+ constexpr bool contains_ptr(const T *ptr) const
{
return (this->begin() <= ptr) && (ptr < this->end());
}
@@ -289,7 +291,7 @@ template<typename T> class Span {
* Does a linear search to count how often the value is in the array.
* Returns the number of occurrences.
*/
- int64_t count(const T &value) const
+ constexpr int64_t count(const T &value) const
{
int64_t counter = 0;
for (const T &element : *this) {
@@ -304,7 +306,7 @@ template<typename T> class Span {
* Return a reference to the first element in the array. This invokes undefined behavior when the
* array is empty.
*/
- const T &first() const
+ constexpr const T &first() const
{
BLI_assert(size_ > 0);
return data_[0];
@@ -314,7 +316,7 @@ template<typename T> class Span {
* Returns a reference to the last element in the array. This invokes undefined behavior when the
* array is empty.
*/
- const T &last() const
+ constexpr const T &last() const
{
BLI_assert(size_ > 0);
return data_[size_ - 1];
@@ -324,7 +326,7 @@ template<typename T> class Span {
* Returns the element at the given index. If the index is out of range, return the fallback
* value.
*/
- T get(int64_t index, const T &fallback) const
+ constexpr T get(int64_t index, const T &fallback) const
{
if (index < size_ && index >= 0) {
return data_[index];
@@ -336,7 +338,7 @@ template<typename T> class Span {
* Check if the array contains duplicates. Does a linear search for every element. So the total
* running time is O(n^2). Only use this for small arrays.
*/
- bool has_duplicates__linear_search() const
+ constexpr bool has_duplicates__linear_search() const
{
/* The size should really be smaller than that. If it is not, the calling code should be
* changed. */
@@ -358,7 +360,7 @@ template<typename T> class Span {
* called on small arrays, because it has a running time of O(n*m) where n and m are the sizes of
* the arrays.
*/
- bool intersects__linear_search(Span other) const
+ constexpr bool intersects__linear_search(Span other) const
{
/* The size should really be smaller than that. If it is not, the calling code should be
* changed. */
@@ -377,7 +379,7 @@ template<typename T> class Span {
* Returns the index of the first occurrence of the given value. This invokes undefined behavior
* when the value is not in the array.
*/
- int64_t first_index(const T &search_value) const
+ constexpr int64_t first_index(const T &search_value) const
{
const int64_t index = this->first_index_try(search_value);
BLI_assert(index >= 0);
@@ -387,7 +389,7 @@ template<typename T> class Span {
/**
* Returns the index of the first occurrence of the given value or -1 if it does not exist.
*/
- int64_t first_index_try(const T &search_value) const
+ constexpr int64_t first_index_try(const T &search_value) const
{
for (int64_t i = 0; i < size_; i++) {
if (data_[i] == search_value) {
@@ -401,7 +403,7 @@ template<typename T> class Span {
* Utility to make it more convenient to iterate over all indices that can be used with this
* array.
*/
- IndexRange index_range() const
+ constexpr IndexRange index_range() const
{
return IndexRange(size_);
}
@@ -409,7 +411,7 @@ template<typename T> class Span {
/**
* Returns a new Span to the same underlying memory buffer. No conversions are done.
*/
- template<typename NewT> Span<NewT> cast() const
+ template<typename NewT> Span<NewT> constexpr cast() const
{
BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0);
int64_t new_size = size_ * sizeof(T) / sizeof(NewT);
@@ -450,21 +452,22 @@ template<typename T> class MutableSpan {
int64_t size_;
public:
- MutableSpan() = default;
+ constexpr MutableSpan() = default;
- MutableSpan(T *start, const int64_t size) : data_(start), size_(size)
+ constexpr MutableSpan(T *start, const int64_t size) : data_(start), size_(size)
{
}
- MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size())
+ constexpr MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size())
{
}
- template<std::size_t N> MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N)
+ template<std::size_t N>
+ constexpr MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N)
{
}
- operator Span<T>() const
+ constexpr operator Span<T>() const
{
return Span<T>(data_, size_);
}
@@ -472,7 +475,7 @@ template<typename T> class MutableSpan {
/**
* Returns the number of elements in the array.
*/
- int64_t size() const
+ constexpr int64_t size() const
{
return size_;
}
@@ -480,7 +483,7 @@ template<typename T> class MutableSpan {
/**
* Replace all elements in the referenced array with the given value.
*/
- void fill(const T &value)
+ constexpr void fill(const T &value)
{
initialized_fill_n(data_, size_, value);
}
@@ -489,7 +492,7 @@ template<typename T> class MutableSpan {
* Replace a subset of all elements with the given value. This invokes undefined behavior when
* one of the indices is out of bounds.
*/
- void fill_indices(Span<int64_t> indices, const T &value)
+ constexpr void fill_indices(Span<int64_t> indices, const T &value)
{
for (int64_t i : indices) {
BLI_assert(i < size_);
@@ -501,30 +504,30 @@ template<typename T> class MutableSpan {
* Returns a pointer to the beginning of the referenced array. This may be nullptr, when the size
* is zero.
*/
- T *data() const
+ constexpr T *data() const
{
return data_;
}
- T *begin() const
+ constexpr T *begin() const
{
return data_;
}
- T *end() const
+ constexpr T *end() const
{
return data_ + size_;
}
- std::reverse_iterator<T *> rbegin() const
+ constexpr std::reverse_iterator<T *> rbegin() const
{
return std::reverse_iterator<T *>(this->end());
}
- std::reverse_iterator<T *> rend() const
+ constexpr std::reverse_iterator<T *> rend() const
{
return std::reverse_iterator<T *>(this->begin());
}
- T &operator[](const int64_t index) const
+ constexpr T &operator[](const int64_t index) const
{
BLI_assert(index < this->size());
return data_[index];
@@ -534,7 +537,7 @@ template<typename T> class MutableSpan {
* Returns a contiguous part of the array. This invokes undefined behavior when the slice would
* go out of bounds.
*/
- MutableSpan slice(const int64_t start, const int64_t length) const
+ constexpr MutableSpan slice(const int64_t start, const int64_t length) const
{
BLI_assert(start + length <= this->size());
return MutableSpan(data_ + start, length);
@@ -544,7 +547,7 @@ template<typename T> class MutableSpan {
* Returns a new MutableSpan with n elements removed from the beginning. This invokes
* undefined behavior when the array is too small.
*/
- MutableSpan drop_front(const int64_t n) const
+ constexpr MutableSpan drop_front(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(n, this->size() - n);
@@ -554,7 +557,7 @@ template<typename T> class MutableSpan {
* Returns a new MutableSpan with n elements removed from the end. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan drop_back(const int64_t n) const
+ constexpr MutableSpan drop_back(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(0, this->size() - n);
@@ -564,7 +567,7 @@ template<typename T> class MutableSpan {
* Returns a new MutableSpan that only contains the first n elements. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan take_front(const int64_t n) const
+ constexpr MutableSpan take_front(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(0, n);
@@ -574,7 +577,7 @@ template<typename T> class MutableSpan {
* Return a new MutableSpan that only contains the last n elements. This invokes undefined
* behavior when the array is too small.
*/
- MutableSpan take_back(const int64_t n) const
+ constexpr MutableSpan take_back(const int64_t n) const
{
BLI_assert(n <= this->size());
return this->slice(this->size() - n, n);
@@ -584,7 +587,7 @@ template<typename T> class MutableSpan {
* Returns an (immutable) Span that references the same array. This is usually not needed,
* due to implicit conversions. However, sometimes automatic type deduction needs some help.
*/
- Span<T> as_span() const
+ constexpr Span<T> as_span() const
{
return Span<T>(data_, size_);
}
@@ -593,7 +596,7 @@ template<typename T> class MutableSpan {
* Utility to make it more convenient to iterate over all indices that can be used with this
* array.
*/
- IndexRange index_range() const
+ constexpr IndexRange index_range() const
{
return IndexRange(size_);
}
@@ -602,7 +605,7 @@ template<typename T> class MutableSpan {
* Returns a reference to the last element. This invokes undefined behavior when the array is
* empty.
*/
- T &last() const
+ constexpr T &last() const
{
BLI_assert(size_ > 0);
return data_[size_ - 1];
@@ -612,7 +615,7 @@ template<typename T> class MutableSpan {
* Does a linear search to count how often the value is in the array.
* Returns the number of occurrences.
*/
- int64_t count(const T &value) const
+ constexpr int64_t count(const T &value) const
{
int64_t counter = 0;
for (const T &element : *this) {
@@ -628,7 +631,7 @@ template<typename T> class MutableSpan {
* destination contains uninitialized data and T is not trivially copy constructible.
* The size of both spans is expected to be the same.
*/
- void copy_from(Span<T> values)
+ constexpr void copy_from(Span<T> values)
{
BLI_assert(size_ == values.size());
initialized_copy_n(values.data(), size_, data_);
@@ -637,7 +640,7 @@ template<typename T> class MutableSpan {
/**
* Returns a new span to the same underlying memory buffer. No conversions are done.
*/
- template<typename NewT> MutableSpan<NewT> cast() const
+ template<typename NewT> constexpr MutableSpan<NewT> cast() const
{
BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0);
int64_t new_size = size_ * sizeof(T) / sizeof(NewT);
@@ -648,7 +651,7 @@ template<typename T> class MutableSpan {
/**
* Utilities to check that arrays have the same size in debug builds.
*/
-template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 &v2)
+template<typename T1, typename T2> constexpr void assert_same_size(const T1 &v1, const T2 &v2)
{
UNUSED_VARS_NDEBUG(v1, v2);
#ifdef DEBUG
@@ -659,7 +662,7 @@ template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2
}
template<typename T1, typename T2, typename T3>
-void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3)
+constexpr void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3)
{
UNUSED_VARS_NDEBUG(v1, v2, v3);
#ifdef DEBUG
diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh
index 8597e54d03b..a2562c6100a 100644
--- a/source/blender/blenlib/BLI_string_ref.hh
+++ b/source/blender/blenlib/BLI_string_ref.hh
@@ -64,7 +64,7 @@ class StringRefBase {
const char *data_;
int64_t size_;
- StringRefBase(const char *data, const int64_t size) : data_(data), size_(size)
+ constexpr StringRefBase(const char *data, const int64_t size) : data_(data), size_(size)
{
}
@@ -75,12 +75,12 @@ class StringRefBase {
/**
* Return the (byte-)length of the referenced string, without any null-terminator.
*/
- int64_t size() const
+ constexpr int64_t size() const
{
return size_;
}
- bool is_empty() const
+ constexpr bool is_empty() const
{
return size_ == 0;
}
@@ -88,12 +88,12 @@ class StringRefBase {
/**
* Return a pointer to the start of the string.
*/
- const char *data() const
+ constexpr const char *data() const
{
return data_;
}
- operator Span<char>() const
+ constexpr operator Span<char>() const
{
return Span<char>(data_, size_);
}
@@ -107,22 +107,22 @@ class StringRefBase {
return std::string(data_, static_cast<size_t>(size_));
}
- operator std::string_view() const
+ constexpr operator std::string_view() const
{
return std::string_view(data_, static_cast<size_t>(size_));
}
- const char *begin() const
+ constexpr const char *begin() const
{
return data_;
}
- const char *end() const
+ constexpr const char *end() const
{
return data_ + size_;
}
- IndexRange index_range() const
+ constexpr IndexRange index_range() const
{
return IndexRange(size_);
}
@@ -165,19 +165,19 @@ class StringRefBase {
/**
* Returns true when the string begins with the given prefix. Otherwise false.
*/
- bool startswith(StringRef prefix) const;
+ constexpr bool startswith(StringRef prefix) const;
/**
* Returns true when the string ends with the given suffix. Otherwise false.
*/
- bool endswith(StringRef suffix) const;
+ constexpr bool endswith(StringRef suffix) const;
- StringRef substr(int64_t start, const int64_t size) const;
+ constexpr StringRef substr(int64_t start, const int64_t size) const;
/**
* Get the first char in the string. This invokes undefined behavior when the string is empty.
*/
- const char &front() const
+ constexpr const char &front() const
{
BLI_assert(size_ >= 1);
return data_[0];
@@ -186,7 +186,7 @@ class StringRefBase {
/**
* Get the last char in the string. This invokes undefined behavior when the string is empty.
*/
- const char &back() const
+ constexpr const char &back() const
{
BLI_assert(size_ >= 1);
return data_[size_ - 1];
@@ -196,18 +196,18 @@ class StringRefBase {
* The behavior of those functions matches the standard library implementation of
* std::string_view.
*/
- int64_t find(char c, int64_t pos = 0) const;
- int64_t find(StringRef str, int64_t pos = 0) const;
- int64_t rfind(char c, int64_t pos = INT64_MAX) const;
- int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const;
- int64_t find_first_of(StringRef chars, int64_t pos = 0) const;
- int64_t find_first_of(char c, int64_t pos = 0) const;
- int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const;
- int64_t find_last_of(char c, int64_t pos = INT64_MAX) const;
- int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const;
- int64_t find_first_not_of(char c, int64_t pos = 0) const;
- int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const;
- int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const;
+ constexpr int64_t find(char c, int64_t pos = 0) const;
+ constexpr int64_t find(StringRef str, int64_t pos = 0) const;
+ constexpr int64_t rfind(char c, int64_t pos = INT64_MAX) const;
+ constexpr int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const;
+ constexpr int64_t find_first_of(StringRef chars, int64_t pos = 0) const;
+ constexpr int64_t find_first_of(char c, int64_t pos = 0) const;
+ constexpr int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const;
+ constexpr int64_t find_last_of(char c, int64_t pos = INT64_MAX) const;
+ constexpr int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const;
+ 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;
};
/**
@@ -216,7 +216,7 @@ class StringRefBase {
class StringRefNull : public StringRefBase {
public:
- StringRefNull() : StringRefBase("", 0)
+ constexpr StringRefNull() : StringRefBase("", 0)
{
}
@@ -226,7 +226,7 @@ class StringRefNull : public StringRefBase {
*/
StringRefNull(const char *str) : StringRefBase(str, static_cast<int64_t>(strlen(str)))
{
- BLI_assert(str != NULL);
+ BLI_assert(str != nullptr);
BLI_assert(data_[size_] == '\0');
}
@@ -234,7 +234,7 @@ class StringRefNull : public StringRefBase {
* Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior
* when the given size is not the correct size of the string.
*/
- StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size)
+ constexpr StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size)
{
BLI_assert(static_cast<int64_t>(strlen(str)) == size);
}
@@ -250,7 +250,7 @@ class StringRefNull : public StringRefBase {
/**
* Get the char at the given index.
*/
- char operator[](const int64_t index) const
+ constexpr char operator[](const int64_t index) const
{
BLI_assert(index >= 0);
/* Use '<=' instead of just '<', so that the null character can be accessed as well. */
@@ -263,7 +263,7 @@ class StringRefNull : public StringRefBase {
*
* This is like ->data(), but can only be called on a StringRefNull.
*/
- const char *c_str() const
+ constexpr const char *c_str() const
{
return data_;
}
@@ -274,25 +274,26 @@ class StringRefNull : public StringRefBase {
*/
class StringRef : public StringRefBase {
public:
- StringRef() : StringRefBase(nullptr, 0)
+ constexpr StringRef() : StringRefBase(nullptr, 0)
{
}
/**
* StringRefNull can be converted into StringRef, but not the other way around.
*/
- StringRef(StringRefNull other) : StringRefBase(other.data(), other.size())
+ constexpr StringRef(StringRefNull other) : StringRefBase(other.data(), other.size())
{
}
/**
* Create a StringRef from a null-terminated c-string.
*/
- StringRef(const char *str) : StringRefBase(str, str ? static_cast<int64_t>(strlen(str)) : 0)
+ constexpr StringRef(const char *str)
+ : StringRefBase(str, str ? static_cast<int64_t>(std::char_traits<char>::length(str)) : 0)
{
}
- StringRef(const char *str, const int64_t length) : StringRefBase(str, length)
+ constexpr StringRef(const char *str, const int64_t length) : StringRefBase(str, length)
{
}
@@ -300,7 +301,7 @@ class StringRef : public StringRefBase {
* Create a StringRef from a start and end pointer. This invokes undefined behavior when the
* second point points to a smaller address than the first one.
*/
- StringRef(const char *begin, const char *one_after_end)
+ constexpr StringRef(const char *begin, const char *one_after_end)
: StringRefBase(begin, static_cast<int64_t>(one_after_end - begin))
{
BLI_assert(begin <= one_after_end);
@@ -314,7 +315,8 @@ class StringRef : public StringRefBase {
{
}
- StringRef(std::string_view view) : StringRefBase(view.data(), static_cast<int64_t>(view.size()))
+ constexpr StringRef(std::string_view view)
+ : StringRefBase(view.data(), static_cast<int64_t>(view.size()))
{
}
@@ -323,7 +325,7 @@ class StringRef : public StringRefBase {
*
* This is similar to std::string_view::remove_prefix.
*/
- StringRef drop_prefix(const int64_t n) const
+ constexpr StringRef drop_prefix(const int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= size_);
@@ -334,7 +336,7 @@ class StringRef : public StringRefBase {
* Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if
* the string does not begin with the given prefix.
*/
- StringRef drop_prefix(StringRef prefix) const
+ constexpr StringRef drop_prefix(StringRef prefix) const
{
BLI_assert(this->startswith(prefix));
return this->drop_prefix(prefix.size());
@@ -345,7 +347,7 @@ class StringRef : public StringRefBase {
*
* This is similar to std::string_view::remove_suffix.
*/
- StringRef drop_suffix(const int64_t n) const
+ constexpr StringRef drop_suffix(const int64_t n) const
{
BLI_assert(n >= 0);
BLI_assert(n <= size_);
@@ -355,7 +357,7 @@ class StringRef : public StringRefBase {
/**
* Get the char at the given index.
*/
- char operator[](int64_t index) const
+ constexpr char operator[](int64_t index) const
{
BLI_assert(index >= 0);
BLI_assert(index < size_);
@@ -391,7 +393,7 @@ inline std::string operator+(StringRef a, StringRef b)
* not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a
* std::string_view, one should convert the std::string_view to StringRef (which is very cheap).
* Ideally, we only use StringRef in our code to avoid this problem altogether. */
-inline bool operator==(StringRef a, StringRef b)
+constexpr inline bool operator==(StringRef a, StringRef b)
{
if (a.size() != b.size()) {
return false;
@@ -399,27 +401,27 @@ inline bool operator==(StringRef a, StringRef b)
return STREQLEN(a.data(), b.data(), (size_t)a.size());
}
-inline bool operator!=(StringRef a, StringRef b)
+constexpr inline bool operator!=(StringRef a, StringRef b)
{
return !(a == b);
}
-inline bool operator<(StringRef a, StringRef b)
+constexpr inline bool operator<(StringRef a, StringRef b)
{
return std::string_view(a) < std::string_view(b);
}
-inline bool operator>(StringRef a, StringRef b)
+constexpr inline bool operator>(StringRef a, StringRef b)
{
return std::string_view(a) > std::string_view(b);
}
-inline bool operator<=(StringRef a, StringRef b)
+constexpr inline bool operator<=(StringRef a, StringRef b)
{
return std::string_view(a) <= std::string_view(b);
}
-inline bool operator>=(StringRef a, StringRef b)
+constexpr inline bool operator>=(StringRef a, StringRef b)
{
return std::string_view(a) >= std::string_view(b);
}
@@ -427,7 +429,7 @@ inline bool operator>=(StringRef a, StringRef b)
/**
* Return true when the string starts with the given prefix.
*/
-inline bool StringRefBase::startswith(StringRef prefix) const
+constexpr inline bool StringRefBase::startswith(StringRef prefix) const
{
if (size_ < prefix.size_) {
return false;
@@ -443,7 +445,7 @@ inline bool StringRefBase::startswith(StringRef prefix) const
/**
* Return true when the string ends with the given suffix.
*/
-inline bool StringRefBase::endswith(StringRef suffix) const
+constexpr inline bool StringRefBase::endswith(StringRef suffix) const
{
if (size_ < suffix.size_) {
return false;
@@ -460,8 +462,8 @@ inline bool StringRefBase::endswith(StringRef suffix) const
/**
* Return a new #StringRef containing only a sub-string of the original string.
*/
-inline StringRef StringRefBase::substr(const int64_t start,
- const int64_t max_size = INT64_MAX) const
+constexpr inline StringRef StringRefBase::substr(const int64_t start,
+ const int64_t max_size = INT64_MAX) const
{
BLI_assert(max_size >= 0);
BLI_assert(start >= 0);
@@ -469,7 +471,7 @@ inline StringRef StringRefBase::substr(const int64_t start,
return StringRef(data_ + start, substr_size);
}
-inline int64_t index_or_npos_to_int64(size_t index)
+constexpr inline int64_t index_or_npos_to_int64(size_t index)
{
/* The compiler will probably optimize this check away. */
if (index == std::string_view::npos) {
@@ -478,62 +480,62 @@ inline int64_t index_or_npos_to_int64(size_t index)
return static_cast<int64_t>(index);
}
-inline int64_t StringRefBase::find(char c, int64_t pos) const
+constexpr inline int64_t StringRefBase::find(char c, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find(StringRef str, int64_t pos) const
+constexpr inline int64_t StringRefBase::find(StringRef str, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_first_of(chars, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const
{
return this->find_first_of(StringRef(&c, 1), pos);
}
-inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_last_of(chars, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const
{
return this->find_last_of(StringRef(&c, 1), pos);
}
-inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_first_not_of(chars, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const
{
return this->find_first_not_of(StringRef(&c, 1), pos);
}
-inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const
{
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_last_not_of(chars, static_cast<size_t>(pos)));
}
-inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const
+constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const
{
return this->find_last_not_of(StringRef(&c, 1), pos);
}
diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h
index d19b5393aa7..eefde1afefb 100644
--- a/source/blender/blenlib/BLI_threads.h
+++ b/source/blender/blenlib/BLI_threads.h
@@ -35,7 +35,6 @@ extern "C" {
#define BLENDER_MAX_THREADS 1024
struct ListBase;
-struct TaskScheduler;
/* Threading API */
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 053dcb6faea..fe6d54ae9e5 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -315,13 +315,13 @@ class Vector {
return MutableSpan<T>(begin_, this->size());
}
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr>
operator Span<U>() const
{
return Span<U>(begin_, this->size());
}
- template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr>
+ template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr>
operator MutableSpan<U>()
{
return MutableSpan<U>(begin_, this->size());
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 46a3ad87dfe..aa4c4efe7d4 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -203,6 +203,7 @@ set(SRC
BLI_heap_simple.h
BLI_index_mask.hh
BLI_index_range.hh
+ BLI_inplace_priority_queue.hh
BLI_iterator.h
BLI_jitter_2d.h
BLI_kdopbvh.h
@@ -390,6 +391,7 @@ if(WITH_GTESTS)
tests/BLI_heap_test.cc
tests/BLI_index_mask_test.cc
tests/BLI_index_range_test.cc
+ tests/BLI_inplace_priority_queue_test.cc
tests/BLI_kdopbvh_test.cc
tests/BLI_linear_allocator_test.cc
tests/BLI_linklist_lockfree_test.cc
diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index f126c5a977b..0f90ad3a490 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -1076,6 +1076,25 @@ float BLI_bvhtree_get_epsilon(const BVHTree *tree)
return tree->epsilon;
}
+/**
+ * This function returns the bounding box of the BVH tree.
+ */
+void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3])
+{
+ BVHNode *root = tree->nodes[tree->totleaf];
+ if (root != NULL) {
+ const float bb_min[3] = {root->bv[0], root->bv[2], root->bv[4]};
+ const float bb_max[3] = {root->bv[1], root->bv[3], root->bv[5]};
+ copy_v3_v3(r_bb_min, bb_min);
+ copy_v3_v3(r_bb_max, bb_max);
+ }
+ else {
+ BLI_assert(false);
+ zero_v3(r_bb_min);
+ zero_v3(r_bb_max);
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index a0ee16bee76..19828e69638 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -372,6 +372,11 @@ void mat3_normalized_to_quat(float q[4], const float mat[3][3])
q[1] = (mat[2][0] + mat[0][2]) * s;
q[2] = (mat[2][1] + mat[1][2]) * s;
}
+
+ /* Make sure w is nonnegative for a canonical result. */
+ if (q[0] < 0) {
+ negate_v4(q);
+ }
}
normalize_qt(q);
@@ -556,10 +561,20 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q
* \param r_twist: if not NULL, receives the twist quaternion.
* \returns twist angle.
*/
-float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4])
+float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4])
{
BLI_assert(axis >= 0 && axis <= 2);
+ /* The calculation requires a canonical quaternion. */
+ float q[4];
+
+ if (q_in[0] < 0) {
+ negate_v4_v4(q, q_in);
+ }
+ else {
+ copy_v4_v4(q, q_in);
+ }
+
/* Half-twist angle can be computed directly. */
float t = atan2f(q[axis + 1], q[0]);
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 2db939cd628..88d90a7816f 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -401,11 +401,6 @@ class Cell {
void add_patch(int p)
{
- patches_.add_new(p);
- }
-
- void add_patch_non_duplicates(int p)
- {
patches_.add(p);
}
@@ -693,7 +688,7 @@ static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesI
merge_to_cell = cinfo.cell(final_merge_to);
}
for (int cell_p : merge_from_cell.patches()) {
- merge_to_cell.add_patch_non_duplicates(cell_p);
+ merge_to_cell.add_patch(cell_p);
Patch &patch = pinfo.patch(cell_p);
if (patch.cell_above == merge_from) {
patch.cell_above = merge_to;
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 74bbe59bc04..5636ffafb6a 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -32,6 +32,7 @@
#include "BLI_fnmatch.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#ifdef WIN32
@@ -287,7 +288,7 @@ void BLI_path_normalize_dir(const char *relabase, char *dir)
* (good practice anyway).
* REMOVED based on popular demand (see T45900).
* Percent '%' char is a bit same case - not recommended to use it,
- * but supported by all decent FS/OS around.
+ * but supported by all decent file-systems/operating-systems around.
*
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
* this can lead to issues.
@@ -512,8 +513,8 @@ void BLI_path_normalize_unc_16(wchar_t *path_16)
#endif
/**
- * Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given
- * the same *relfile, will convert it back to its original value.
+ * Replaces `file` with a relative version (prefixed by "//") such that #BLI_path_abs, given
+ * the same `relfile`, will convert it back to its original value.
*/
void BLI_path_rel(char *file, const char *relfile)
{
@@ -663,7 +664,7 @@ void BLI_path_rel(char *file, const char *relfile)
* \param maxlen: Maximum length of string
* \param suffix: String to append to the original string
* \param sep: Optional separator character
- * \return true if succeeded
+ * \return true if succeeded
*/
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
{
@@ -702,7 +703,7 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char
/**
* Replaces path with the path of its parent directory, returning true if
- * it was able to find a parent directory within the pathname.
+ * it was able to find a parent directory within the path.
*/
bool BLI_path_parent_dir(char *path)
{
@@ -721,7 +722,7 @@ bool BLI_path_parent_dir(char *path)
}
/**
- * Strips off nonexistent (or non-accessible) subdirectories from the end of *dir,
+ * Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`,
* leaving the path of the lowest-level directory that does exist and we can read.
*/
bool BLI_path_parent_dir_until_exists(char *dir)
@@ -736,9 +737,9 @@ bool BLI_path_parent_dir_until_exists(char *dir)
}
/**
- * Looks for a sequence of "#" characters in the last slash-separated component of *path,
+ * Looks for a sequence of "#" characters in the last slash-separated component of `path`,
* returning the indexes of the first and one past the last character in the sequence in
- * *char_start and *char_end respectively. Returns true if such a sequence was found.
+ * `char_start` and `char_end` respectively. Returns true if such a sequence was found.
*/
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
{
@@ -773,7 +774,7 @@ static bool stringframe_chars(const char *path, int *char_start, int *char_end)
}
/**
- * Ensure *path contains at least one "#" character in its last slash-separated
+ * Ensure `path` contains at least one "#" character in its last slash-separated
* component, appending one digits long if not.
*/
static void ensure_digits(char *path, int digits)
@@ -795,7 +796,7 @@ static void ensure_digits(char *path, int digits)
}
/**
- * Replaces "#" character sequence in last slash-separated component of *path
+ * Replaces "#" character sequence in last slash-separated component of `path`
* with frame as decimal integer, with leading zeroes as necessary, to make digits digits.
*/
bool BLI_path_frame(char *path, int frame, int digits)
@@ -817,7 +818,7 @@ bool BLI_path_frame(char *path, int frame, int digits)
}
/**
- * Replaces "#" character sequence in last slash-separated component of *path
+ * Replaces "#" character sequence in last slash-separated component of `path`
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
* digits each, with a hyphen in-between.
*/
@@ -962,7 +963,7 @@ bool BLI_path_frame_check_chars(const char *path)
/**
* Creates a display string from path to be used menus and the user interface.
- * Like bpy.path.display_name().
+ * Like `bpy.path.display_name()`.
*/
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
{
@@ -1003,7 +1004,7 @@ void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
}
/**
- * If path begins with "//", strips that and replaces it with basepath directory.
+ * If path begins with "//", strips that and replaces it with `basepath` directory.
*
* \note Also converts drive-letter prefix to something more sensible
* if this is a non-drive-letter-based system.
@@ -1161,7 +1162,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
#ifdef _WIN32
/**
* Tries appending each of the semicolon-separated extensions in the PATHEXT
- * environment variable (Windows-only) onto *name in turn until such a file is found.
+ * environment variable (Windows-only) onto `name` in turn until such a file is found.
* Returns success/failure.
*/
bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
@@ -1268,7 +1269,7 @@ bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *na
/**
* Sets the specified environment variable to the specified value,
- * and clears it if val == NULL.
+ * and clears it if `val == NULL`.
*/
void BLI_setenv(const char *env, const char *val)
{
@@ -1304,30 +1305,44 @@ void BLI_setenv_if_new(const char *env, const char *val)
/**
* Get an env var, result has to be used immediately.
*
- * On windows getenv gets its variables from a static copy of the environment variables taken at
+ * On windows #getenv gets its variables from a static copy of the environment variables taken at
* process start-up, causing it to not pick up on environment variables created during runtime.
* This function uses an alternative method to get environment variables that does pick up on
- * runtime environment variables.
+ * runtime environment variables. The result will be UTF-8 encoded.
*/
const char *BLI_getenv(const char *env)
{
#ifdef _MSC_VER
- static char buffer[32767]; /* 32767 is the total size of the environment block on windows*/
- if (GetEnvironmentVariableA(env, buffer, sizeof(buffer))) {
- return buffer;
- }
- else {
- return NULL;
+ const char *result = NULL;
+ /* 32767 is the maximum size of the environment variable on windows,
+ * reserve one more character for the zero terminator. */
+ static wchar_t buffer[32768];
+ wchar_t *env_16 = alloc_utf16_from_8(env, 0);
+ if (env_16) {
+ if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
+ char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
+ /* Make sure the result is valid, and will fit into our temporary storage buffer. */
+ if (res_utf8) {
+ if (strlen(res_utf8) + 1 < sizeof(buffer)) {
+ /* We are re-using the utf16 buffer here, since allocating a second static buffer to
+ * contain the UTF-8 version to return would be wasteful. */
+ memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
+ result = (const char *)buffer;
+ }
+ free(res_utf8);
+ }
+ }
}
+ return result;
#else
return getenv(env);
#endif
}
/**
- * Ensures that the parent directory of *name exists.
+ * Ensures that the parent directory of `name` exists.
*
- * \return true on success (i.e. given path now exists on FS), false otherwise.
+ * \return true on success (i.e. given path now exists on file-system), false otherwise.
*/
bool BLI_make_existing_file(const char *name)
{
@@ -1339,13 +1354,13 @@ bool BLI_make_existing_file(const char *name)
}
/**
- * Returns in *string the concatenation of *dir and *file (also with *relabase on the
- * front if specified and *dir begins with "//"). Normalizes all occurrences of path
- * separators, including ensuring there is exactly one between the copies of *dir and *file,
- * and between the copies of *relabase and *dir.
+ * Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the
+ * front if specified and `dir` begins with "//"). Normalizes all occurrences of path
+ * separators, including ensuring there is exactly one between the copies of `dir` and `file`,
+ * and between the copies of `relabase` and `dir`.
*
- * \param relabase: Optional prefix to substitute for "//" on front of *dir
- * \param string: Area to return result
+ * \param relabase: Optional prefix to substitute for "//" on front of `dir`.
+ * \param string: Area to return result.
*/
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
{
@@ -1485,9 +1500,8 @@ bool BLI_path_extension_check_array(const char *str, const char **ext_array)
}
/**
- * Semicolon separated wildcards, eg:
- * '*.zip;*.py;*.exe'
- * does str match any of the semicolon-separated glob patterns in fnmatch.
+ * Semicolon separated wildcards, eg: `*.zip;*.py;*.exe`
+ * does str match any of the semicolon-separated glob patterns in #fnmatch.
*/
bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
{
@@ -1516,10 +1530,11 @@ bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
}
/**
- * Does basic validation of the given glob string, to prevent common issues from string truncation.
+ * Does basic validation of the given glob string, to prevent common issues from string
+ * truncation.
*
* For now, only forbids last group to be a wildcard-only one, if there are more than one group
- * (i.e. things like "*.txt;*.cpp;*" are changed to "*.txt;*.cpp;")
+ * (i.e. things like `*.txt;*.cpp;*` are changed to `*.txt;*.cpp;`)
*
* \returns true if it had to modify given \a ext_fnmatch pattern.
*/
@@ -1629,7 +1644,7 @@ bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filenam
}
/**
- * Converts `/foo/bar.txt` to "/foo/" and `bar.txt`
+ * Converts `/foo/bar.txt` to `/foo/` and `bar.txt`
*
* - Wont change \a string.
* - Wont create any directories.
@@ -1662,7 +1677,7 @@ void BLI_split_dirfile(
}
/**
- * Copies the parent directory part of string into *dir, max length dirlen.
+ * Copies the parent directory part of string into `dir`, max length `dirlen`.
*/
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
{
@@ -1670,7 +1685,7 @@ void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
}
/**
- * Copies the leaf filename part of string into *file, max length filelen.
+ * Copies the leaf filename part of string into `file`, max length `filelen`.
*/
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
{
@@ -1717,7 +1732,7 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re
/**
* Simple appending of filename to dir, does not check for valid path!
- * Puts result into *dst, which may be same area as *dir.
+ * Puts result into `dst`, which may be same area as `dir`.
*/
void BLI_join_dirfile(char *__restrict dst,
const size_t maxlen,
@@ -1761,7 +1776,7 @@ void BLI_join_dirfile(char *__restrict dst,
* Join multiple strings into a path, ensuring only a single path separator between each,
* and trailing slash is kept.
*
- * \note If you want a trailing slash, add ``SEP_STR`` as the last path argument,
+ * \note If you want a trailing slash, add `SEP_STR` as the last path argument,
* duplicate slashes will be cleaned up.
*/
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
@@ -1845,7 +1860,7 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat
}
/**
- * like pythons os.path.basename()
+ * like Python's `os.path.basename()`
*
* \return The pointer into \a path string immediately after last slash,
* or start of \a path if none found.
diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc
index d472ded0f18..ddaa067f50e 100644
--- a/source/blender/blenlib/tests/BLI_index_range_test.cc
+++ b/source/blender/blenlib/tests/BLI_index_range_test.cc
@@ -140,4 +140,11 @@ TEST(index_range, AsSpan)
EXPECT_EQ(span[3], 7);
}
+TEST(index_range, constexpr_)
+{
+ constexpr IndexRange range = IndexRange(1, 1);
+ std::array<int, range[0]> compiles = {1};
+ BLI_STATIC_ASSERT(range.size() == 1, "");
+ EXPECT_EQ(compiles[0], 1);
+}
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc
new file mode 100644
index 00000000000..3adf12f36a7
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc
@@ -0,0 +1,113 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_inplace_priority_queue.hh"
+#include "BLI_rand.hh"
+
+namespace blender::tests {
+
+TEST(inplace_priority_queue, BuildSmall)
+{
+ Array<int> values = {1, 5, 2, 8, 5, 6, 5, 4, 3, 6, 7, 3};
+ InplacePriorityQueue<int> priority_queue{values};
+
+ EXPECT_EQ(priority_queue.peek(), 8);
+ EXPECT_EQ(priority_queue.pop(), 8);
+ EXPECT_EQ(priority_queue.peek(), 7);
+ EXPECT_EQ(priority_queue.pop(), 7);
+ EXPECT_EQ(priority_queue.pop(), 6);
+ EXPECT_EQ(priority_queue.pop(), 6);
+ EXPECT_EQ(priority_queue.pop(), 5);
+}
+
+TEST(inplace_priority_queue, DecreasePriority)
+{
+ Array<int> values = {5, 2, 7, 4};
+ InplacePriorityQueue<int> priority_queue(values);
+
+ EXPECT_EQ(priority_queue.peek(), 7);
+ values[2] = 0;
+ EXPECT_EQ(priority_queue.peek(), 0);
+ priority_queue.priority_decreased(2);
+ EXPECT_EQ(priority_queue.peek(), 5);
+}
+
+TEST(inplace_priority_queue, IncreasePriority)
+{
+ Array<int> values = {5, 2, 7, 4};
+ InplacePriorityQueue<int> priority_queue(values);
+
+ EXPECT_EQ(priority_queue.peek(), 7);
+ values[1] = 10;
+ EXPECT_EQ(priority_queue.peek(), 7);
+ priority_queue.priority_increased(1);
+ EXPECT_EQ(priority_queue.peek(), 10);
+}
+
+TEST(inplace_priority_queue, PopAll)
+{
+ RandomNumberGenerator rng;
+ Vector<int> values;
+ const int amount = 1000;
+ for (int i = 0; i < amount; i++) {
+ values.append(rng.get_int32() % amount);
+ }
+
+ InplacePriorityQueue<int> priority_queue(values);
+
+ int last_value = amount;
+ while (!priority_queue.is_empty()) {
+ const int value = priority_queue.pop();
+ EXPECT_LE(value, last_value);
+ last_value = value;
+ }
+}
+
+TEST(inplace_priority_queue, ManyPriorityChanges)
+{
+ RandomNumberGenerator rng;
+ Vector<int> values;
+ const int amount = 1000;
+ for (int i = 0; i < amount; i++) {
+ values.append(rng.get_int32() % amount);
+ }
+
+ InplacePriorityQueue<int> priority_queue(values);
+
+ for (int i = 0; i < amount; i++) {
+ const int index = rng.get_int32() % amount;
+ const int new_priority = rng.get_int32() % amount;
+ values[index] = new_priority;
+ priority_queue.priority_changed(index);
+ }
+
+ int last_value = amount;
+ while (!priority_queue.is_empty()) {
+ const int value = priority_queue.pop();
+ EXPECT_LE(value, last_value);
+ last_value = value;
+ }
+}
+
+TEST(inplace_priority_queue, IndicesAccess)
+{
+ Array<int> values = {4, 6, 2, 4, 8, 1, 10, 2, 5};
+ InplacePriorityQueue<int> priority_queue(values);
+
+ EXPECT_EQ(priority_queue.active_indices().size(), 9);
+ EXPECT_EQ(priority_queue.inactive_indices().size(), 0);
+ EXPECT_EQ(priority_queue.all_indices().size(), 9);
+ EXPECT_EQ(priority_queue.pop(), 10);
+ EXPECT_EQ(priority_queue.active_indices().size(), 8);
+ EXPECT_EQ(priority_queue.inactive_indices().size(), 1);
+ EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 10);
+ EXPECT_EQ(priority_queue.all_indices().size(), 9);
+ EXPECT_EQ(priority_queue.pop(), 8);
+ EXPECT_EQ(priority_queue.inactive_indices().size(), 2);
+ EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 8);
+ EXPECT_EQ(values[priority_queue.inactive_indices()[1]], 10);
+ EXPECT_EQ(priority_queue.all_indices().size(), 9);
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
index 02257ba83dd..5a179bff3d6 100644
--- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
@@ -2,6 +2,7 @@
#include "testing/testing.h"
+#include "BLI_math_base.h"
#include "BLI_math_rotation.h"
#include <cmath>
@@ -71,6 +72,12 @@ TEST(math_rotation, quat_to_mat_to_quat_bad_T83196)
test_quat_to_mat_to_quat(0.0149f, 0.9996f, -0.0212f, -0.0107f);
}
+TEST(math_rotation, quat_to_mat_to_quat_bad_negative)
+{
+ /* This shouldn't produce a negative q[0]. */
+ test_quat_to_mat_to_quat(0.5f - 1e-6f, 0, -sqrtf(3) / 2 - 1e-6f, 0);
+}
+
TEST(math_rotation, quat_to_mat_to_quat_near_1000)
{
test_quat_to_mat_to_quat(0.9999f, 0.01f, -0.001f, -0.01f);
@@ -126,3 +133,17 @@ TEST(math_rotation, quat_to_mat_to_quat_near_0001)
test_quat_to_mat_to_quat(0.25f, -0.025f, -0.25f, 0.97f);
test_quat_to_mat_to_quat(0.30f, -0.030f, -0.30f, 0.95f);
}
+
+TEST(math_rotation, quat_split_swing_and_twist_negative)
+{
+ const float input[4] = {-0.5f, 0, sqrtf(3) / 2, 0};
+ const float expected_swing[4] = {1.0f, 0, 0, 0};
+ const float expected_twist[4] = {0.5f, 0, -sqrtf(3) / 2, 0};
+ float swing[4], twist[4];
+
+ float twist_angle = quat_split_swing_and_twist(input, 1, swing, twist);
+
+ EXPECT_NEAR(twist_angle, -M_PI * 2 / 3, FLT_EPSILON);
+ EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON);
+ EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
+}
diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
index fcef2f8688a..23415e69b04 100644
--- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc
+++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc
@@ -158,4 +158,15 @@ static_assert(is_convertible_pointer_v<int **, int **const>);
static_assert(is_convertible_pointer_v<int **, int *const *>);
static_assert(is_convertible_pointer_v<int **, int const *const *>);
+static_assert(is_span_convertible_pointer_v<int *, int *>);
+static_assert(is_span_convertible_pointer_v<int *, const int *>);
+static_assert(!is_span_convertible_pointer_v<const int *, int *>);
+static_assert(is_span_convertible_pointer_v<const int *, const int *>);
+static_assert(is_span_convertible_pointer_v<const int *, const void *>);
+static_assert(!is_span_convertible_pointer_v<const int *, void *>);
+static_assert(is_span_convertible_pointer_v<int *, void *>);
+static_assert(is_span_convertible_pointer_v<int *, const void *>);
+static_assert(!is_span_convertible_pointer_v<TestBaseClass *, TestChildClass *>);
+static_assert(!is_span_convertible_pointer_v<TestChildClass *, TestBaseClass *>);
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc
index 9a8d9df7873..d1c9f312b97 100644
--- a/source/blender/blenlib/tests/BLI_span_test.cc
+++ b/source/blender/blenlib/tests/BLI_span_test.cc
@@ -337,4 +337,19 @@ TEST(span, MutableReverseIterator)
EXPECT_EQ_ARRAY(src.data(), Span({14, 15, 16, 17}).data(), 4);
}
+TEST(span, constexpr_)
+{
+ static constexpr std::array<int, 3> src = {3, 2, 1};
+ constexpr Span<int> span(src);
+ BLI_STATIC_ASSERT(span[2] == 1, "");
+ BLI_STATIC_ASSERT(span.size() == 3, "");
+ BLI_STATIC_ASSERT(span.slice(1, 2).size() == 2, "");
+ BLI_STATIC_ASSERT(span.has_duplicates__linear_search() == false, "");
+
+ std::integral_constant<bool, span.first_index(1) == 2> ic;
+ BLI_STATIC_ASSERT(ic.value, "");
+
+ EXPECT_EQ(span.slice(1, 2).size(), 2);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc
index 2d488feff71..401a7bc1118 100644
--- a/source/blender/blenlib/tests/BLI_string_ref_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc
@@ -298,4 +298,12 @@ TEST(string_ref, ToStringView)
EXPECT_EQ(view, "hello");
}
+TEST(string_ref, constexpr_)
+{
+ constexpr StringRef sref("World");
+ BLI_STATIC_ASSERT(sref[2] == 'r', "");
+ BLI_STATIC_ASSERT(sref.size() == 5, "");
+ std::array<int, static_cast<std::size_t>(sref.find_first_of('o'))> compiles = {1};
+ EXPECT_EQ(compiles[0], 1);
+}
} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h
index c4480e2c544..ea0532d884b 100644
--- a/source/blender/blenloader/BLO_read_write.h
+++ b/source/blender/blenloader/BLO_read_write.h
@@ -160,6 +160,7 @@ void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_p
void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr);
void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr);
void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr);
+void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr);
void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr);
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr);
void BLO_write_string(BlendWriter *writer, const char *data_ptr);
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 0ab9a5e9e14..1d7c5d8a1d3 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -119,12 +119,20 @@ void BLO_blendfiledata_free(BlendFileData *bfd);
/** \name BLO Blend File Handle API
* \{ */
+struct BLODataBlockInfo {
+ char name[64]; /* MAX_NAME */
+ struct AssetMetaData *asset_data;
+};
+
BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct ReportList *reports);
BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize);
struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
int ofblocktype,
int *tot_names);
+struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
+ int ofblocktype,
+ int *tot_info_items);
struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *tot_prev);
struct LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh);
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index 1aecba5ba90..296480fc2e4 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -40,6 +40,7 @@
#include "DNA_genfile.h"
#include "DNA_sdna_types.h"
+#include "BKE_icons.h"
#include "BKE_idtype.h"
#include "BKE_main.h"
@@ -160,6 +161,51 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype,
}
/**
+ * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain
+ * type (e.g. all the scene names in a file).
+ *
+ * \param bh: The blendhandle to access.
+ * \param ofblocktype: The type of names to get.
+ * \param tot_info_items: The length of the returned list.
+ * \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should
+ * be freed with MEM_freeN.
+ */
+LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, int *tot_info_items)
+{
+ FileData *fd = (FileData *)bh;
+ LinkNode *infos = NULL;
+ BHead *bhead;
+ int tot = 0;
+
+ for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
+ if (bhead->code == ofblocktype) {
+ struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
+ const char *name = blo_bhead_id_name(fd, bhead) + 2;
+
+ STRNCPY(info->name, name);
+
+ /* Lastly, read asset data from the following blocks. */
+ info->asset_data = blo_bhead_id_asset_data_address(fd, bhead);
+ if (info->asset_data) {
+ bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data);
+ /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next
+ * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
+ bhead = blo_bhead_prev(fd, bhead);
+ }
+
+ BLI_linklist_prepend(&infos, info);
+ tot++;
+ }
+ else if (bhead->code == ENDB) {
+ break;
+ }
+ }
+
+ *tot_info_items = tot;
+ return infos;
+}
+
+/**
* Gets the previews of all the data-blocks in a file of a certain type
* (e.g. all the scene previews in a file).
*
@@ -203,6 +249,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to
if (looking) {
if (bhead->SDNAnr == DNA_struct_find_nr(fd->filesdna, "PreviewImage")) {
prv = BLO_library_read_struct(fd, bhead, "PreviewImage");
+
if (prv) {
memcpy(new_prv, prv, sizeof(PreviewImage));
if (prv->rect[0] && prv->w[0] && prv->h[0]) {
@@ -217,6 +264,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to
new_prv->rect[0] = NULL;
new_prv->w[0] = new_prv->h[0] = 0;
}
+ BKE_previewimg_finish(new_prv, 0);
if (prv->rect[1] && prv->w[1] && prv->h[1]) {
bhead = blo_bhead_next(fd, bhead);
@@ -230,6 +278,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to
new_prv->rect[1] = NULL;
new_prv->w[1] = new_prv->h[1] = 0;
}
+ BKE_previewimg_finish(new_prv, 1);
MEM_freeN(prv);
}
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 9ce767b7ce1..bccc7150523 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -44,7 +44,9 @@
#define DNA_DEPRECATED_ALLOW
#include "DNA_anim_types.h"
+#include "DNA_asset_types.h"
#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
#include "DNA_fileglobal_types.h"
#include "DNA_genfile.h"
#include "DNA_key_types.h"
@@ -53,6 +55,7 @@
#include "DNA_packedFile_types.h"
#include "DNA_sdna_types.h"
#include "DNA_sound_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_volume_types.h"
#include "DNA_workspace_types.h"
@@ -71,6 +74,7 @@
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
+#include "BKE_asset.h"
#include "BKE_collection.h"
#include "BKE_global.h" /* for G */
#include "BKE_idprop.h"
@@ -955,12 +959,21 @@ static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock)
}
#endif /* USE_BHEAD_READ_ON_DEMAND */
-/* Warning! Caller's responsibility to ensure given bhead **is** and ID one! */
+/* Warning! Caller's responsibility to ensure given bhead **is** an ID one! */
const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead)
{
return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs);
}
+/* Warning! Caller's responsibility to ensure given bhead **is** an ID one! */
+AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *bhead)
+{
+ BLI_assert(BKE_idtype_idcode_is_valid(bhead->code));
+ return (fd->id_asset_data_offs >= 0) ?
+ *(AssetMetaData **)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_asset_data_offs) :
+ NULL;
+}
+
static void decode_blender_header(FileData *fd)
{
char header[SIZEOFBLENDERHEADER], num[4];
@@ -1038,6 +1051,8 @@ static bool read_file_dna(FileData *fd, const char **r_error_message)
/* used to retrieve ID names from (bhead+1) */
fd->id_name_offs = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]");
BLI_assert(fd->id_name_offs != -1);
+ fd->id_asset_data_offs = DNA_elem_offset(
+ fd->filesdna, "ID", "AssetMetaData", "*asset_data");
return true;
}
@@ -2358,6 +2373,11 @@ static void direct_link_id_common(
return;
}
+ if (id->asset_data) {
+ BLO_read_data_address(reader, &id->asset_data);
+ BKE_asset_metadata_read(reader, id->asset_data);
+ }
+
/*link direct data of ID properties*/
if (id->properties) {
BLO_read_data_address(reader, &id->properties);
@@ -2731,6 +2751,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
SpaceFile *sfile = (SpaceFile *)sl;
sfile->op = NULL;
sfile->previews_timer = NULL;
+ sfile->tags = FILE_TAG_REBUILD_MAIN_FILES;
}
else if (sl->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)sl;
@@ -3606,6 +3627,27 @@ static BHead *read_libblock(FileData *fd,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Read Asset Data
+ * \{ */
+
+BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_asset_data)
+{
+ BLI_assert(BKE_idtype_idcode_is_valid(bhead->code));
+
+ bhead = read_data_into_datamap(fd, bhead, "asset-data read");
+
+ BlendDataReader reader = {fd};
+ BLO_read_data_address(&reader, r_asset_data);
+ BKE_asset_metadata_read(&reader, *r_asset_data);
+
+ oldnewmap_clear(fd->datamap);
+
+ return bhead;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Read Global Data
* \{ */
@@ -3863,6 +3905,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
BLO_read_list(reader, &user->user_menus);
BLO_read_list(reader, &user->addons);
BLO_read_list(reader, &user->autoexec_paths);
+ BLO_read_list(reader, &user->asset_libraries);
LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
keymap->modal_items = NULL;
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index fb950e37da8..c724cc32051 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -34,16 +34,13 @@
#include "zlib.h"
struct BLOCacheStorage;
-struct GSet;
struct IDNameLib_Map;
struct Key;
struct MemFile;
struct Object;
struct OldNewMap;
-struct PartEff;
struct ReportList;
struct UserDef;
-struct View3D;
typedef struct IDNameLib_Map IDNameLib_Map;
@@ -112,6 +109,9 @@ typedef struct FileData {
int fileversion;
/** Used to retrieve ID names from (bhead+1). */
int id_name_offs;
+ /** Used to retrieve asset data from (bhead+1). NOTE: This may not be available in old files,
+ * will be -1 then! */
+ int id_asset_data_offs;
/** For do_versions patching. */
int globalf, fileflags;
@@ -159,6 +159,8 @@ void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain);
void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd);
void blo_make_old_idmap_from_main(FileData *fd, struct Main *bmain);
+BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, struct AssetMetaData **r_asset_data);
+
void blo_cache_storage_init(FileData *fd, struct Main *bmain);
void blo_cache_storage_old_bmain_clear(FileData *fd, struct Main *bmain_old);
void blo_cache_storage_end(FileData *fd);
@@ -170,6 +172,7 @@ BHead *blo_bhead_next(FileData *fd, BHead *thisblock);
BHead *blo_bhead_prev(FileData *fd, BHead *thisblock);
const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead);
+struct AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *bhead);
/* do versions stuff */
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index c86ad639216..b282a978e8a 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -46,6 +46,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_sdna_types.h"
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index c33f2a8cad5..c336239ec59 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -28,11 +28,13 @@
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
#include "DNA_genfile.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_fluidsim_types.h"
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index adc2b55b350..046749e4691 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -64,6 +64,7 @@
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
+#include "DNA_material_types.h"
#include "SEQ_sequencer.h"
@@ -301,7 +302,9 @@ static void do_version_hue_sat_node(bNodeTree *ntree, bNode *node)
/* Take care of possible animation. */
AnimData *adt = BKE_animdata_from_id(&ntree->id);
if (adt != NULL && adt->action != NULL) {
- const char *prefix = BLI_sprintfN("nodes[\"%s\"]", node->name);
+ char node_name_esc[sizeof(node->name) * 2];
+ BLI_str_escape(node_name_esc, node->name, sizeof(node_name_esc));
+ const char *prefix = BLI_sprintfN("nodes[\"%s\"]", node_name_esc);
for (FCurve *fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) {
if (STRPREFIX(fcu->rna_path, prefix)) {
anim_change_prop_name(fcu, prefix, "color_hue", "inputs[1].default_value");
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index a98e7c46f10..4df681002a0 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -29,6 +29,7 @@
#include "DNA_anim_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_fluid_types.h"
#include "DNA_genfile.h"
@@ -52,6 +53,7 @@
#include "BKE_armature.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
+#include "BKE_cryptomatte.h"
#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
#include "BKE_lib_id.h"
@@ -72,6 +74,29 @@
/* Make preferences read-only, use versioning_userdef.c. */
#define U (*((const UserDef *)&U))
+static eSpaceSeq_Proxy_RenderSize get_sequencer_render_size(Main *bmain)
+{
+ eSpaceSeq_Proxy_RenderSize render_size = 100;
+
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ switch (sl->spacetype) {
+ case SPACE_SEQ: {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
+ render_size = sseq->render_size;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return render_size;
+}
+
/* image_size is width or height depending what RNA property is converted - X or Y. */
static void seq_convert_transform_animation(const Scene *scene,
const char *path,
@@ -210,6 +235,84 @@ static void seq_convert_transform_crop_lb(const Scene *scene,
}
}
+static void seq_convert_transform_animation_2(const Scene *scene,
+ const char *path,
+ const float scale_to_fit_factor)
+{
+ if (scene->adt == NULL || scene->adt->action == NULL) {
+ return;
+ }
+
+ FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0);
+ if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) {
+ BezTriple *bezt = fcu->bezt;
+ for (int i = 0; i < fcu->totvert; i++, bezt++) {
+ /* Same math as with old_image_center_*, but simplified. */
+ bezt->vec[1][1] *= scale_to_fit_factor;
+ }
+ }
+}
+
+static void seq_convert_transform_crop_2(const Scene *scene,
+ Sequence *seq,
+ const eSpaceSeq_Proxy_RenderSize render_size)
+{
+ const StripElem *s_elem = SEQ_render_give_stripelem(seq, seq->start);
+ if (s_elem == NULL) {
+ return;
+ }
+
+ StripCrop *c = seq->strip->crop;
+ StripTransform *t = seq->strip->transform;
+ int image_size_x = s_elem->orig_width;
+ int image_size_y = s_elem->orig_height;
+
+ if (SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(render_size))) {
+ image_size_x /= SEQ_rendersize_to_scale_factor(render_size);
+ image_size_y /= SEQ_rendersize_to_scale_factor(render_size);
+ }
+
+ /* Calculate scale factor, so image fits in preview area with original aspect ratio. */
+ const float scale_to_fit_factor = MIN2((float)scene->r.xsch / (float)image_size_x,
+ (float)scene->r.ysch / (float)image_size_y);
+ t->scale_x *= scale_to_fit_factor;
+ t->scale_y *= scale_to_fit_factor;
+ c->top /= scale_to_fit_factor;
+ c->bottom /= scale_to_fit_factor;
+ c->left /= scale_to_fit_factor;
+ c->right /= scale_to_fit_factor;
+
+ char name_esc[(sizeof(seq->name) - 2) * 2], *path;
+ BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc));
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_x", name_esc);
+ seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
+ MEM_freeN(path);
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc);
+ seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
+ MEM_freeN(path);
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_y", name_esc);
+ seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
+ MEM_freeN(path);
+ path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc);
+ seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
+ MEM_freeN(path);
+}
+
+static void seq_convert_transform_crop_lb_2(const Scene *scene,
+ const ListBase *lb,
+ const eSpaceSeq_Proxy_RenderSize render_size)
+{
+
+ LISTBASE_FOREACH (Sequence *, seq, lb) {
+ if (seq->type != SEQ_TYPE_SOUND_RAM) {
+ seq_convert_transform_crop_2(scene, seq, render_size);
+ }
+ if (seq->type == SEQ_TYPE_META) {
+ seq_convert_transform_crop_lb_2(scene, &seq->seqbase, render_size);
+ }
+ }
+}
+
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@@ -439,25 +542,35 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 292, 2)) {
- eSpaceSeq_Proxy_RenderSize render_size = 100;
+ eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain);
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- switch (sl->spacetype) {
- case SPACE_SEQ: {
- SpaceSeq *sseq = (SpaceSeq *)sl;
- render_size = sseq->render_size;
- break;
- }
- }
- }
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size);
}
}
+ }
+ if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) {
+ /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in
+ * Armature obdata. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type == OB_ARMATURE) {
+ BKE_pose_rebuild(bmain, ob, ob->data, true);
+ }
+ }
+
+ /* Wet Paint Radius Factor */
+ for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
+ if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) {
+ br->wet_paint_radius_factor = 1.0f;
+ }
+ }
+
+ eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain);
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
- seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size);
+ seq_convert_transform_crop_lb_2(scene, &scene->ed->seqbase, render_size);
}
}
}
@@ -474,21 +587,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
-
- /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in
- * Armature obdata. */
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- if (ob->type == OB_ARMATURE) {
- BKE_pose_rebuild(bmain, ob, ob->data, true);
- }
- }
- }
-
- /* Wet Paint Radius Factor */
- for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
- if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) {
- br->wet_paint_radius_factor = 1.0f;
- }
}
}
@@ -1232,6 +1330,93 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 292, 7)) {
+ /* Make all IDProperties used as interface of geometry node trees overridable. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ IDProperty *nmd_properties = nmd->settings.properties;
+
+ BLI_assert(nmd_properties->type == IDP_GROUP);
+ LISTBASE_FOREACH (IDProperty *, nmd_socket_idprop, &nmd_properties->data.group) {
+ nmd_socket_idprop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
+ }
+ }
+ }
+ }
+
+ /* EEVEE/Cycles Volumes consistency */
+ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ /* Remove Volume Transmittance render pass from each view layer. */
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ view_layer->eevee.render_passes &= ~EEVEE_RENDER_PASS_UNUSED_8;
+ }
+
+ /* Rename Renderlayer Socket `VolumeScatterCol` to `VolumeDir` */
+ if (scene->nodetree) {
+ LISTBASE_FOREACH (bNode *, node, &scene->nodetree->nodes) {
+ if (node->type == CMP_NODE_R_LAYERS) {
+ LISTBASE_FOREACH (bNodeSocket *, output_socket, &node->outputs) {
+ const char *volume_scatter = "VolumeScatterCol";
+ if (STREQLEN(output_socket->name, volume_scatter, MAX_NAME)) {
+ BLI_strncpy(output_socket->name, RE_PASSNAME_VOLUME_LIGHT, MAX_NAME);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Convert `NodeCryptomatte->storage->matte_id` to `NodeCryptomatte->storage->entries` */
+ if (!DNA_struct_find(fd->filesdna, "CryptomatteEntry")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->nodetree) {
+ LISTBASE_FOREACH (bNode *, node, &scene->nodetree->nodes) {
+ if (node->type == CMP_NODE_CRYPTOMATTE) {
+ NodeCryptomatte *storage = (NodeCryptomatte *)node->storage;
+ char *matte_id = storage->matte_id;
+ if (matte_id == NULL || strlen(storage->matte_id) == 0) {
+ continue;
+ }
+ BKE_cryptomatte_matte_id_to_entries(NULL, storage, storage->matte_id);
+ MEM_SAFE_FREE(storage->matte_id);
+ }
+ }
+ }
+ }
+ }
+
+ /* Overlay elements in the sequencer. */
+ 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_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_SOURCE |
+ SEQ_SHOW_STRIP_DURATION);
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
+ STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c
index 19e392734f0..631abe10ddc 100644
--- a/source/blender/blenloader/intern/versioning_cycles.c
+++ b/source/blender/blenloader/intern/versioning_cycles.c
@@ -840,12 +840,14 @@ static void update_mapping_node_fcurve_rna_path_callback(ID *UNUSED(id),
fcurve->rna_path = BLI_sprintfN("%s.%s", data->nodePath, "inputs[3].default_value");
}
else if (data->minimumNode && BLI_str_endswith(old_fcurve_rna_path, "max")) {
- fcurve->rna_path = BLI_sprintfN(
- "nodes[\"%s\"].%s", data->minimumNode->name, "inputs[1].default_value");
+ char node_name_esc[sizeof(data->minimumNode->name) * 2];
+ BLI_str_escape(node_name_esc, data->minimumNode->name, sizeof(node_name_esc));
+ fcurve->rna_path = BLI_sprintfN("nodes[\"%s\"].%s", node_name_esc, "inputs[1].default_value");
}
else if (data->maximumNode && BLI_str_endswith(old_fcurve_rna_path, "min")) {
- fcurve->rna_path = BLI_sprintfN(
- "nodes[\"%s\"].%s", data->maximumNode->name, "inputs[1].default_value");
+ char node_name_esc[sizeof(data->maximumNode->name) * 2];
+ BLI_str_escape(node_name_esc, data->maximumNode->name, sizeof(node_name_esc));
+ fcurve->rna_path = BLI_sprintfN("nodes[\"%s\"].%s", node_name_esc, "inputs[1].default_value");
}
if (fcurve->rna_path != old_fcurve_rna_path) {
@@ -955,7 +957,10 @@ static void update_mapping_node_inputs_and_properties(bNodeTree *ntree)
MEM_freeN(node->storage);
node->storage = NULL;
- char *nodePath = BLI_sprintfN("nodes[\"%s\"]", node->name);
+ char node_name_esc[sizeof(node->name) * 2];
+ BLI_str_escape(node_name_esc, node->name, sizeof(node_name_esc));
+
+ char *nodePath = BLI_sprintfN("nodes[\"%s\"]", node_name_esc);
MappingNodeFCurveCallbackData data = {nodePath, minimumNode, maximumNode};
BKE_fcurves_id_cb(&ntree->id, update_mapping_node_fcurve_rna_path_callback, &data);
MEM_freeN(nodePath);
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 48a24755250..198f65b9794 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -37,6 +37,7 @@
#include "DNA_curveprofile_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_light_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -181,7 +182,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->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;
}
else if (area->spacetype == SPACE_TEXT) {
/* Show syntax and line numbers in Script workspace text editor. */
@@ -737,6 +739,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER;
}
+ brush_name = "Multires Displacement Smear";
+ brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+ if (!brush) {
+ brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+ id_us_min(&brush->id);
+ brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR;
+ }
+
/* Use the same tool icon color in the brush cursor */
for (brush = bmain->brushes.first; brush; brush = brush->id.next) {
if (brush->ob_mode & OB_MODE_SCULPT) {
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index d654a0e30bd..6f459339675 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -49,6 +49,7 @@
#include "DNA_nla_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_sdna_types.h"
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 7bc11317bb4..f1572c563bf 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -31,6 +31,7 @@
#endif
#include "DNA_anim_types.h"
+#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
@@ -43,6 +44,7 @@
#include "BKE_idprop.h"
#include "BKE_keyconfig.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BLO_readfile.h"
@@ -328,9 +330,6 @@ void blo_do_versions_userdef(UserDef *userdef)
#define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver)
/* the UserDef struct is not corrected with do_versions() .... ugh! */
- if (userdef->wheellinescroll == 0) {
- userdef->wheellinescroll = 3;
- }
if (userdef->menuthreshold1 == 0) {
userdef->menuthreshold1 = 5;
userdef->menuthreshold2 = 2;
@@ -832,6 +831,9 @@ void blo_do_versions_userdef(UserDef *userdef)
*/
{
/* Keep this block, even when empty. */
+ if (BLI_listbase_is_empty(&userdef->asset_libraries)) {
+ BKE_preferences_asset_library_default_add(userdef);
+ }
}
LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) {
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 11fe240620a..0a4f2fde93f 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -93,6 +93,7 @@
/* allow writefile to use deprecated functionality (for forward compatibility code) */
#define DNA_DEPRECATED_ALLOW
+#include "DNA_collection_types.h"
#include "DNA_fileglobal_types.h"
#include "DNA_genfile.h"
#include "DNA_sdna_types.h"
@@ -755,6 +756,10 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
BLO_write_struct(writer, bPathCompare, path_cmp);
}
+ LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) {
+ BLO_write_struct(writer, bUserAssetLibrary, asset_library);
+ }
+
LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
BLO_write_struct(writer, uiStyle, style);
}
@@ -1369,6 +1374,11 @@ void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
BLO_write_raw(writer, sizeof(float) * (size_t)num, data_ptr);
}
+void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
+{
+ BLO_write_raw(writer, sizeof(double) * (size_t)num, data_ptr);
+}
+
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
{
BLO_write_raw(writer, sizeof(void *) * (size_t)num, data_ptr);
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
index bfb093c569f..ea5d66e195c 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.cc
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -296,7 +296,7 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden
BMIter liter;
BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
while (l != nullptr) {
- BM_loop_interp_from_face(bm, l, orig_face, true, true);
+ BM_loop_interp_from_face(bm, l, orig_face, false, true);
l = static_cast<BMLoop *>(BM_iter_step(&liter));
}
}
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp
index 8cc6b933759..7ca4e1f76fc 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp
@@ -17,12 +17,15 @@
*/
#include "COM_CryptomatteNode.h"
-#include "BLI_assert.h"
-#include "BLI_hash_mm3.h"
-#include "BLI_string.h"
#include "COM_ConvertOperation.h"
#include "COM_CryptomatteOperation.h"
#include "COM_SetAlphaOperation.h"
+
+#include "BLI_assert.h"
+#include "BLI_hash_mm3.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
#include <iterator>
CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode)
@@ -30,24 +33,6 @@ CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode)
/* pass */
}
-/* This is taken from the Cryptomatte specification 1.0. */
-static inline float hash_to_float(uint32_t hash)
-{
- uint32_t mantissa = hash & ((1 << 23) - 1);
- uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
- exponent = max(exponent, (uint32_t)1);
- exponent = min(exponent, (uint32_t)254);
- exponent = exponent << 23;
- uint32_t sign = (hash >> 31);
- sign = sign << 31;
- uint32_t float_bits = sign | exponent | mantissa;
- float f;
- /* Bit casting relies on equal size for both types. */
- BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size")
- ::memcpy(&f, &float_bits, sizeof(float));
- return f;
-}
-
void CryptomatteNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -61,30 +46,8 @@ void CryptomatteNode::convertToOperations(NodeConverter &converter,
CryptomatteOperation *operation = new CryptomatteOperation(getNumberOfInputSockets() - 1);
if (cryptoMatteSettings) {
- if (cryptoMatteSettings->matte_id) {
- /* Split the string by commas, ignoring white space. */
- std::string input = cryptoMatteSettings->matte_id;
- std::istringstream ss(input);
- while (ss.good()) {
- std::string token;
- getline(ss, token, ',');
- /* Ignore empty tokens. */
- if (token.length() > 0) {
- size_t first = token.find_first_not_of(' ');
- size_t last = token.find_last_not_of(' ');
- if (first == std::string::npos || last == std::string::npos) {
- break;
- }
- token = token.substr(first, (last - first + 1));
- if (*token.begin() == '<' && *(--token.end()) == '>') {
- operation->addObjectIndex(atof(token.substr(1, token.length() - 2).c_str()));
- }
- else {
- uint32_t hash = BLI_hash_mm3((const unsigned char *)token.c_str(), token.length(), 0);
- operation->addObjectIndex(hash_to_float(hash));
- }
- }
- }
+ LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptoMatteSettings->entries) {
+ operation->addObjectIndex(cryptomatte_entry->encoded_hash);
}
}
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index f894bdabba4..4e618d8625d 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -39,7 +39,6 @@ struct Main;
struct Object;
struct Scene;
struct Simulation;
-struct ViewLayer;
struct bNodeTree;
#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 5af70305e13..e5301532ddc 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -1483,6 +1483,9 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_IMAGE) {
build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value);
}
+ else if (socket->type == SOCK_COLLECTION) {
+ build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value);
+ }
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index c7669b9fecb..b1fd86f13bc 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -34,6 +34,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "DNA_collection_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_layer_types.h"
#include "DNA_linestyle_types.h"
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 11d34782569..de68ec6210e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -39,6 +39,7 @@
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
+#include "DNA_cloth_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
@@ -2316,6 +2317,12 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_image(image);
}
}
+ else if (socket->type == SOCK_COLLECTION) {
+ Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value;
+ if (collection != nullptr) {
+ build_collection(nullptr, nullptr, collection);
+ }
+ }
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index cbfb51c59a6..5587379089c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -45,7 +45,6 @@
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
-struct Base;
struct CacheFile;
struct Camera;
struct Collection;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
index 8df8d4914c3..24876049942 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
@@ -34,6 +34,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "DNA_collection_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index b92bf475f49..e472d82f2ee 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -136,8 +136,8 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return false;
}
- if (data->geometry_component_owner->type != OB_POINTCLOUD) {
- /* Only point clouds support multiple geometry components currently. */
+ if (data->geometry_component_owner->runtime.geometry_set_eval == nullptr) {
+ /* Return the object itself, if it does not have a geometry set yet. */
iter->current = data->geometry_component_owner;
data->geometry_component_owner = nullptr;
return true;
@@ -149,10 +149,16 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return false;
}
+ /* The mesh component. */
if (data->geometry_component_id == 0) {
data->geometry_component_id++;
- /* The mesh component. */
+ /* Don't use a temporary object for this component, when the owner is a mesh object. */
+ if (data->geometry_component_owner->type == OB_MESH) {
+ iter->current = data->geometry_component_owner;
+ return true;
+ }
+
const Mesh *mesh = geometry_set->get_mesh_for_read();
if (mesh != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
@@ -164,10 +170,17 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return true;
}
}
+
+ /* The pointcloud component. */
if (data->geometry_component_id == 1) {
data->geometry_component_id++;
- /* The pointcloud component. */
+ /* Don't use a temporary object for this component, when the owner is a point cloud object. */
+ if (data->geometry_component_owner->type == OB_POINTCLOUD) {
+ iter->current = data->geometry_component_owner;
+ return true;
+ }
+
const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read();
if (pointcloud != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
@@ -179,6 +192,7 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
return true;
}
}
+
data->geometry_component_owner = nullptr;
return false;
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
index 1f58c54dbf4..ec661360fdf 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
@@ -25,8 +25,6 @@
#pragma once
-struct Main;
-
namespace blender {
namespace deg {
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 44ff86b3333..9150de7184a 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -125,7 +125,7 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
return;
}
if (eevee_cryptomatte_active_layers(view_layer) != 0) {
- g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE;
+ g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT;
g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag &
VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0;
}
@@ -137,7 +137,8 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
- EEVEE_PrivateData *g_data = vedata->stl->g_data;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_PrivateData *g_data = stl->g_data;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
@@ -303,7 +304,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s
GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
- vedata, sldata, ob, false, NULL);
+ vedata, sldata, ob, NULL, false);
DRW_shgroup_call(grp, geom, ob);
}
}
@@ -469,6 +470,8 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PrivateData *g_data = stl->g_data;
+ EEVEE_EffectsInfo *effects = stl->effects;
+ EEVEE_TextureList *txl = vedata->txl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const ViewLayer *view_layer = draw_ctx->view_layer;
const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer);
@@ -478,11 +481,25 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer;
BLI_assert(accum_buffer);
+ float *volumetric_transmittance_buffer = NULL;
+ if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
+ volumetric_transmittance_buffer = GPU_texture_read(
+ txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0);
+ }
+ const int num_samples = effects->taa_current_sample;
+
int accum_pixel_index = 0;
int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer);
for (int pixel_index = 0; pixel_index < buffer_size;
pixel_index++, accum_pixel_index += accum_pixel_stride) {
+ float coverage = 1.0f;
+ if (volumetric_transmittance_buffer != NULL) {
+ coverage = (volumetric_transmittance_buffer[pixel_index * 4] +
+ volumetric_transmittance_buffer[pixel_index * 4 + 1] +
+ volumetric_transmittance_buffer[pixel_index * 4 + 2]) /
+ (3.0f * num_samples);
+ }
for (int layer = 0; layer < num_cryptomatte_layers; layer++) {
const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer);
/* Calculate the total weight of the sample. */
@@ -493,24 +510,40 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata)
}
BLI_assert(total_weight > 0.0f);
- float total_weight_inv = 1.0f / total_weight;
- for (int level = 0; level < num_levels; level++) {
- EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level];
- /* Remove background samples. These samples were used to determine the correct weight
- * but won't be part of the final result. */
- if (sample->hash == 0.0f) {
+ float total_weight_inv = coverage / total_weight;
+ if (total_weight_inv > 0.0f) {
+ for (int level = 0; level < num_levels; level++) {
+ EEVEE_CryptomatteSample *sample =
+ &accum_buffer[accum_pixel_index + layer_offset + level];
+ /* Remove background samples. These samples were used to determine the correct weight
+ * but won't be part of the final result. */
+ if (sample->hash == 0.0f) {
+ sample->weight = 0.0f;
+ }
+ sample->weight *= total_weight_inv;
+ }
+
+ /* Sort accum buffer by coverage of each sample. */
+ qsort(&accum_buffer[accum_pixel_index + layer_offset],
+ num_levels,
+ sizeof(EEVEE_CryptomatteSample),
+ eevee_cryptomatte_sample_cmp_reverse);
+ }
+ else {
+ /* This pixel doesn't have any weight, so clear it fully. */
+ for (int level = 0; level < num_levels; level++) {
+ EEVEE_CryptomatteSample *sample =
+ &accum_buffer[accum_pixel_index + layer_offset + level];
sample->weight = 0.0f;
+ sample->hash = 0.0f;
}
- sample->weight *= total_weight_inv;
}
-
- /* Sort accum buffer by coverage of each sample. */
- qsort(&accum_buffer[accum_pixel_index + layer_offset],
- num_levels,
- sizeof(EEVEE_CryptomatteSample),
- eevee_cryptomatte_sample_cmp_reverse);
}
}
+
+ if (volumetric_transmittance_buffer) {
+ MEM_freeN(volumetric_transmittance_buffer);
+ }
}
/* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.h b/source/blender/draw/engines/eevee/eevee_lightcache.h
index 17392c0de0b..fde0c80ab37 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.h
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.h
@@ -24,14 +24,14 @@
#include "BLI_sys_types.h" /* for bool */
+struct BlendDataReader;
+struct BlendWriter;
struct EEVEE_Data;
struct EEVEE_ViewLayerData;
struct LightCache;
struct Scene;
struct SceneEEVEE;
struct ViewLayer;
-struct BlendWriter;
-struct BlendDataReader;
/* Light Bake */
struct wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm,
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index a6a480ca967..5bf8cab1b22 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -989,6 +989,7 @@ typedef struct EEVEE_PrivateData {
GPUTexture *renderpass_input;
GPUTexture *renderpass_col_input;
GPUTexture *renderpass_light_input;
+ GPUTexture *renderpass_transmittance_input;
/* Renderpass ubo reference used by material pass. */
struct GPUUniformBuf *renderpass_ubo;
/** For rendering shadows. */
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 2b2ff2e5c90..2a01aeefce8 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -461,21 +461,13 @@ static void eevee_render_result_environment(RenderLayer *rl,
EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT)
}
-static void eevee_render_result_volume_scatter(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
-{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_SCATTER, VOLUME_SCATTER)
-}
-static void eevee_render_result_volume_transmittance(RenderLayer *rl,
- const char *viewname,
- const rcti *rect,
- EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata)
+static void eevee_render_result_volume_light(RenderLayer *rl,
+ const char *viewname,
+ const rcti *rect,
+ EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata)
{
- EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_TRANSMITTANCE, VOLUME_TRANSMITTANCE)
+ EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT)
}
static void eevee_render_result_aovs(RenderLayer *rl,
@@ -696,8 +688,7 @@ void EEVEE_render_read_result(EEVEE_Data *vedata,
eevee_render_result_emission(rl, viewname, rect, vedata, sldata);
eevee_render_result_environment(rl, viewname, rect, vedata, sldata);
eevee_render_result_bloom(rl, viewname, rect, vedata, sldata);
- eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata);
- eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata);
+ eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata);
eevee_render_result_aovs(rl, viewname, rect, vedata, sldata);
eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata);
}
@@ -730,8 +721,7 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v
CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(VOLUME_SCATTER, SOCK_RGBA, 3, "RGB");
- CHECK_PASS_EEVEE(VOLUME_TRANSMITTANCE, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(VOLUME_LIGHT, 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_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index e7a03c678a8..dff3b437953 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -44,14 +44,14 @@ typedef enum eRenderPassPostProcessType {
PASS_POST_AO = 6,
PASS_POST_NORMAL = 7,
PASS_POST_TWO_LIGHT_BUFFERS = 8,
+ PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR = 9,
} eRenderPassPostProcessType;
/* bitmask containing all renderpasses that need post-processing */
#define EEVEE_RENDERPASSES_WITH_POST_PROCESSING \
(EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_MIST | EEVEE_RENDER_PASS_NORMAL | \
- EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_SCATTER | \
- EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_SHADOW | \
- EEVEE_RENDERPASSES_MATERIAL)
+ EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_LIGHT | \
+ EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDERPASSES_MATERIAL)
#define EEVEE_RENDERPASSES_ALL \
(EEVEE_RENDERPASSES_WITH_POST_PROCESSING | EEVEE_RENDER_PASS_COMBINED)
@@ -64,7 +64,10 @@ typedef enum eRenderPassPostProcessType {
EEVEE_RENDER_PASS_BLOOM)
#define EEVEE_RENDERPASSES_LIGHT_PASS \
(EEVEE_RENDER_PASS_DIFFUSE_LIGHT | EEVEE_RENDER_PASS_SPECULAR_LIGHT)
-
+/* Render passes that uses volume transmittance when available */
+#define EEVEE_RENDERPASSES_USES_TRANSMITTANCE \
+ (EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \
+ EEVEE_RENDER_PASS_ENVIRONMENT)
bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
@@ -147,6 +150,18 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata)
EEVEE_cryptomatte_renderpasses_init(vedata);
}
+BLI_INLINE bool eevee_renderpasses_volumetric_active(const EEVEE_EffectsInfo *effects,
+ const EEVEE_PrivateData *g_data)
+{
+ if (effects->enabled_effects & EFFECT_VOLUMETRIC) {
+ if (g_data->render_passes &
+ (EEVEE_RENDER_PASS_VOLUME_LIGHT | EEVEE_RENDERPASSES_USES_TRANSMITTANCE)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_Data *vedata,
uint tot_samples)
@@ -189,8 +204,7 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_bloom_output_init(sldata, vedata, tot_samples);
}
- if ((g_data->render_passes &
- (EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) {
+ if (eevee_renderpasses_volumetric_active(effects, g_data)) {
EEVEE_volumes_output_init(sldata, vedata, tot_samples);
}
@@ -198,6 +212,7 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata,
g_data->renderpass_input = txl->color;
g_data->renderpass_col_input = txl->color;
g_data->renderpass_light_input = txl->color;
+ g_data->renderpass_transmittance_input = txl->color;
}
else {
/* Free unneeded memory */
@@ -227,6 +242,8 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve
DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input);
DRW_shgroup_uniform_texture_ref(
grp, "inputSecondLightBuffer", &g_data->renderpass_light_input);
+ DRW_shgroup_uniform_texture_ref(
+ grp, "inputTransmittanceBuffer", &g_data->renderpass_transmittance_input);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo);
DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined);
@@ -265,6 +282,19 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
g_data->renderpass_current_sample = current_sample;
g_data->renderpass_type = renderpass_type;
g_data->renderpass_postprocess = PASS_POST_UNDEFINED;
+ const bool volumetric_active = eevee_renderpasses_volumetric_active(effects, g_data);
+ eRenderPassPostProcessType default_color_pass_type =
+ volumetric_active ? PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR : PASS_POST_ACCUMULATED_COLOR;
+ g_data->renderpass_transmittance_input = volumetric_active ? txl->volume_transmittance_accum :
+ txl->color;
+
+ if (!volumetric_active && renderpass_type == EEVEE_RENDER_PASS_VOLUME_LIGHT) {
+ /* Early exit: Volumetric effect is off, but the volume light pass was requested. */
+ static float clear_col[4] = {0.0f};
+ GPU_framebuffer_bind(fbl->renderpass_fb);
+ GPU_framebuffer_clear_color(fbl->renderpass_fb, clear_col);
+ return;
+ }
switch (renderpass_type) {
case EEVEE_RENDER_PASS_Z: {
@@ -286,38 +316,33 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata),
g_data->renderpass_input = txl->mist_accum;
break;
}
- case EEVEE_RENDER_PASS_VOLUME_SCATTER: {
+ case EEVEE_RENDER_PASS_VOLUME_LIGHT: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
g_data->renderpass_input = txl->volume_scatter_accum;
break;
}
- case EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE: {
- g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
- g_data->renderpass_input = txl->volume_transmittance_accum;
- break;
- }
case EEVEE_RENDER_PASS_SHADOW: {
g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE;
g_data->renderpass_input = txl->shadow_accum;
break;
}
case EEVEE_RENDER_PASS_DIFFUSE_COLOR: {
- g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
+ g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->diff_color_accum;
break;
}
case EEVEE_RENDER_PASS_SPECULAR_COLOR: {
- g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
+ g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->spec_color_accum;
break;
}
case EEVEE_RENDER_PASS_ENVIRONMENT: {
- g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
+ g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->env_accum;
break;
}
case EEVEE_RENDER_PASS_EMIT: {
- g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR;
+ g_data->renderpass_postprocess = default_color_pass_type;
g_data->renderpass_input = txl->emit_accum;
break;
}
@@ -372,7 +397,8 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_EffectsInfo *effects = stl->effects;
- eViewLayerEEVEEPassType render_pass = stl->g_data->render_passes;
+ EEVEE_PrivateData *g_data = stl->g_data;
+ eViewLayerEEVEEPassType render_pass = g_data->render_passes;
if (!post_effect) {
if ((render_pass & EEVEE_RENDER_PASS_MIST) != 0) {
@@ -387,8 +413,7 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata,
if ((render_pass & EEVEE_RENDERPASSES_MATERIAL) != 0) {
EEVEE_material_output_accumulate(sldata, vedata);
}
- if ((render_pass &
- (EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) {
+ if (eevee_renderpasses_volumetric_active(effects, g_data)) {
EEVEE_volumes_output_accumulate(sldata, vedata);
}
if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) {
diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
index 1e499dbf991..9426b8e4a7b 100644
--- a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl
@@ -4,4 +4,4 @@ out vec4 fragColor;
void main()
{
fragColor = cryptohash;
-} \ No newline at end of file
+}
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
index eb6ca4b9de8..0bbbe58c44a 100644
--- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl
@@ -11,6 +11,7 @@
#define PASS_POST_AO 6
#define PASS_POST_NORMAL 7
#define PASS_POST_TWO_LIGHT_BUFFERS 8
+#define PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR 9
uniform int postProcessType;
uniform int currentSample;
@@ -19,6 +20,7 @@ uniform sampler2D depthBuffer;
uniform sampler2D inputBuffer;
uniform sampler2D inputSecondLightBuffer;
uniform sampler2D inputColorBuffer;
+uniform sampler2D inputTransmittanceBuffer;
out vec4 fragColor;
@@ -99,6 +101,11 @@ void main()
vec4 accumulated_color = texelFetch(inputBuffer, texel, 0);
color = (accumulated_color / currentSample);
}
+ else if (postProcessType == PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR) {
+ vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb;
+ vec3 transmittance = texelFetch(inputTransmittanceBuffer, texel, 0).rgb;
+ color.rgb = (accumulated_color / currentSample) * (transmittance / currentSample);
+ }
else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) {
vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb;
vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb;
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 04128dc157e..d0bd56b42dd 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -39,10 +39,7 @@ extern DrawEngineType draw_engine_gpencil_type;
struct GPENCIL_Data;
struct GPENCIL_StorageList;
struct GPUBatch;
-struct GPUVertBuf;
-struct GPUVertFormat;
struct GpencilBatchCache;
-struct MaterialGPencilStyle;
struct Object;
struct RenderEngine;
struct RenderLayer;
diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h
index d5821cc5d70..76a94e68da1 100644
--- a/source/blender/draw/engines/image/image_private.h
+++ b/source/blender/draw/engines/image/image_private.h
@@ -25,11 +25,9 @@ extern "C" {
#endif
/* Forward declarations */
-struct GPUBatch;
struct GPUTexture;
struct ImBuf;
struct Image;
-struct rcti;
/* *********** LISTS *********** */
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl
index 1d936e4e072..ea2715a3c32 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl
@@ -1,6 +1,5 @@
-uniform vec4 outlineColor;
-
+in vec4 outlineColor;
in vec4 radii;
in vec4 fillColor;
out vec4 fragColor;
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl
index 9e9df82a77d..cb70a3b433c 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl
@@ -26,7 +26,7 @@ void main()
/* Move selected vertices to the top
* Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4
* actual pixels are at 0.75, 1.0 is used for the background. */
- float depth = is_selected ? 0.05 : 0.15;
+ float depth = is_selected ? (is_pinned ? 0.05 : 0.10) : 0.15;
gl_Position = vec4(point_world_to_ndc(world_pos).xy, depth, 1.0);
gl_PointSize = pointSize;
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index fbe71900915..4df2ba1e913 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -64,7 +64,6 @@
#include "DEG_depsgraph.h"
struct GPUBatch;
-struct GPUFrameBuffer;
struct GPUMaterial;
struct GPUShader;
struct GPUTexture;
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index 75685c7e2f0..a9febcedbf9 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -38,6 +38,7 @@
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
+#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 23dd47d4ab5..a059ac32311 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -22,10 +22,8 @@
#pragma once
-struct DRWPass;
struct DRWShadingGroup;
struct FluidModifierData;
-struct GPUMaterial;
struct ModifierData;
struct Object;
struct ParticleSystem;
diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c
index f1598ea2fff..ba03cee8149 100644
--- a/source/blender/draw/intern/draw_instance_data.c
+++ b/source/blender/draw/intern/draw_instance_data.c
@@ -669,10 +669,14 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr,
DupliObject *dupli_source,
float r_data[4])
{
- char idprop_name[sizeof(attr->name) + 4];
-
copy_v4_fl(r_data, 0);
- sprintf(idprop_name, "[\"%s\"]", attr->name);
+
+ char idprop_name[(sizeof(attr->name) * 2) + 4];
+ {
+ char attr_name_esc[sizeof(attr->name) * 2];
+ BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc));
+ SNPRINTF(idprop_name, "[\"%s\"]", attr_name_esc);
+ }
/* If requesting instance data, check the parent particle system and object. */
if (attr->use_dupli) {
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 988975bd399..ffc565d0514 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -2073,7 +2073,6 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph,
* for the image editor this is when showing UV's.*/
const bool do_populate_loop = (DST.draw_ctx.space_data->spacetype == SPACE_IMAGE);
const bool do_annotations = drw_draw_show_annotation();
- const bool do_region_callbacks = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE);
const bool do_draw_gizmos = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE);
/* Get list of enabled engines */
@@ -2125,7 +2124,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph,
/* Start Drawing */
DRW_state_reset();
- if (do_region_callbacks && DST.draw_ctx.evil_C) {
+ if (DST.draw_ctx.evil_C) {
ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_PRE_VIEW);
}
@@ -2147,10 +2146,8 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph,
if (do_annotations) {
ED_annotation_draw_view2d(DST.draw_ctx.evil_C, true);
}
- if (do_region_callbacks) {
- GPU_depth_test(GPU_DEPTH_NONE);
- ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW);
- }
+ GPU_depth_test(GPU_DEPTH_NONE);
+ ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW);
GPU_matrix_pop_projection();
/* Callback can be nasty and do whatever they want with the state.
* Don't trust them! */
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 1f5dc73f732..a2ae350ce4b 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -22,6 +22,7 @@ if(WITH_BLENDER)
add_subdirectory(animation)
add_subdirectory(armature)
+ add_subdirectory(asset)
add_subdirectory(curve)
add_subdirectory(geometry)
add_subdirectory(gizmo_library)
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 9e622aea6ab..0db3e984b60 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -503,7 +503,7 @@ static void ANIM_OT_previewrange_clear(wmOperatorType *ot)
/* identifiers */
ot->name = "Clear Preview Range";
ot->idname = "ANIM_OT_previewrange_clear";
- ot->description = "Clear Preview Range";
+ ot->description = "Clear preview range";
/* api callbacks */
ot->exec = previewrange_clear_exec;
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index b20d2738bda..f2cb00f67f0 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -1014,7 +1014,7 @@ void ARMATURE_OT_switch_direction(wmOperatorType *ot)
/* identifiers */
ot->name = "Switch Direction";
ot->idname = "ARMATURE_OT_switch_direction";
- ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)";
+ ot->description = "Change the direction that a chain of bones points in (head and tail swap)";
/* api callbacks */
ot->exec = armature_switch_direction_exec;
diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c
index c217b615db6..7c11c5e537e 100644
--- a/source/blender/editors/armature/editarmature_undo.c
+++ b/source/blender/editors/armature/editarmature_undo.c
@@ -26,7 +26,9 @@
#include "CLG_log.h"
#include "DNA_armature_types.h"
+#include "DNA_layer_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
new file mode 100644
index 00000000000..63a1761b264
--- /dev/null
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -0,0 +1,39 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenlib
+ ../../blenkernel
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ asset_edit.c
+ asset_ops.c
+)
+
+set(LIB
+)
+
+blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/asset/asset_edit.c b/source/blender/editors/asset/asset_edit.c
new file mode 100644
index 00000000000..5333c08c66a
--- /dev/null
+++ b/source/blender/editors/asset/asset_edit.c
@@ -0,0 +1,69 @@
+/*
+ * 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_asset.h"
+#include "BKE_context.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+
+#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+
+#include "UI_interface_icons.h"
+
+#include "RNA_access.h"
+
+#include "ED_asset.h"
+
+bool ED_asset_mark_id(const bContext *C, ID *id)
+{
+ if (id->asset_data) {
+ return false;
+ }
+ if (!BKE_id_can_be_asset(id)) {
+ return false;
+ }
+
+ id_fake_user_set(id);
+
+ id->asset_data = BKE_asset_metadata_create();
+
+ UI_icon_render_id(C, NULL, id, true, true);
+
+ return true;
+}
+
+bool ED_asset_clear_id(ID *id)
+{
+ if (!id->asset_data) {
+ return false;
+ }
+ BKE_asset_metadata_free(&id->asset_data);
+ /* 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. */
+
+ return true;
+}
+
+bool ED_asset_can_make_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 != NULL;
+}
diff --git a/source/blender/editors/asset/asset_ops.c b/source/blender/editors/asset/asset_ops.c
new file mode 100644
index 00000000000..929d49e19fa
--- /dev/null
+++ b/source/blender/editors/asset/asset_ops.c
@@ -0,0 +1,238 @@
+/*
+ * 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 <string.h>
+
+#include "BKE_asset.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string_utils.h"
+
+#include "DNA_asset_types.h"
+
+#include "ED_asset.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* -------------------------------------------------------------------- */
+
+struct AssetMarkResultStats {
+ int tot_created;
+ int tot_already_asset;
+ ID *last_id;
+};
+
+/**
+ * Return the IDs to operate on as list of #CollectionPointerLink links. Needs freeing.
+ */
+static ListBase /* CollectionPointerLink */ asset_operation_get_ids_from_context(const bContext *C)
+{
+ ListBase list = {0};
+
+ PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
+
+ if (idptr.data) {
+ CollectionPointerLink *ctx_link = MEM_callocN(sizeof(*ctx_link), __func__);
+ ctx_link->ptr = idptr;
+ BLI_addtail(&list, ctx_link);
+ }
+ else {
+ CTX_data_selected_ids(C, &list);
+ }
+
+ return list;
+}
+
+static void asset_mark_for_idptr_list(const bContext *C,
+ const ListBase /* CollectionPointerLink */ *ids,
+ struct AssetMarkResultStats *r_stats)
+{
+ memset(r_stats, 0, sizeof(*r_stats));
+
+ LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
+ BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
+
+ ID *id = ctx_id->ptr.data;
+ if (id->asset_data) {
+ r_stats->tot_already_asset++;
+ continue;
+ }
+
+ if (ED_asset_mark_id(C, id)) {
+ r_stats->last_id = id;
+ r_stats->tot_created++;
+ }
+ }
+}
+
+static bool asset_mark_results_report(const struct AssetMarkResultStats *stats,
+ ReportList *reports)
+{
+ /* User feedback on failure. */
+ if ((stats->tot_created < 1) && (stats->tot_already_asset > 0)) {
+ BKE_report(reports,
+ RPT_ERROR,
+ "Selected data-blocks are already assets (or do not support use as assets)");
+ return false;
+ }
+ if (stats->tot_created < 1) {
+ BKE_report(reports,
+ RPT_ERROR,
+ "No data-blocks to create assets for found (or do not support use as assets)");
+ return false;
+ }
+
+ /* User feedback on success. */
+ if (stats->tot_created == 1) {
+ /* If only one data-block: Give more useful message by printing asset name. */
+ BKE_reportf(reports, RPT_INFO, "Data-block '%s' is now an asset", stats->last_id->name + 2);
+ }
+ else {
+ BKE_reportf(reports, RPT_INFO, "%i data-blocks are now assets", stats->tot_created);
+ }
+
+ return true;
+}
+
+static int asset_mark_exec(bContext *C, wmOperator *op)
+{
+ ListBase ids = asset_operation_get_ids_from_context(C);
+
+ struct AssetMarkResultStats stats;
+ asset_mark_for_idptr_list(C, &ids, &stats);
+ BLI_freelistN(&ids);
+
+ if (!asset_mark_results_report(&stats, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_mark(wmOperatorType *ot)
+{
+ ot->name = "Mark 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)";
+ ot->idname = "ASSET_OT_mark";
+
+ ot->exec = asset_mark_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------------------------------------- */
+
+struct AssetClearResultStats {
+ int tot_removed;
+ ID *last_id;
+};
+
+static void asset_clear_from_idptr_list(const ListBase /* CollectionPointerLink */ *ids,
+ struct AssetClearResultStats *r_stats)
+{
+ memset(r_stats, 0, sizeof(*r_stats));
+
+ LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
+ BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
+
+ ID *id = ctx_id->ptr.data;
+ if (!id->asset_data) {
+ continue;
+ }
+
+ if (ED_asset_clear_id(id)) {
+ r_stats->tot_removed++;
+ r_stats->last_id = id;
+ }
+ }
+}
+
+static bool asset_clear_result_report(const struct AssetClearResultStats *stats,
+ ReportList *reports)
+
+{
+ if (stats->tot_removed < 1) {
+ BKE_report(reports, RPT_ERROR, "No asset data-blocks selected/focused");
+ return false;
+ }
+
+ if (stats->tot_removed == 1) {
+ /* If only one data-block: Give more useful message by printing asset name. */
+ BKE_reportf(
+ reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats->last_id->name + 2);
+ }
+ else {
+ BKE_reportf(reports, RPT_INFO, "%i data-blocks are no assets anymore", stats->tot_removed);
+ }
+
+ return true;
+}
+
+static int asset_clear_exec(bContext *C, wmOperator *op)
+{
+ ListBase ids = asset_operation_get_ids_from_context(C);
+
+ struct AssetClearResultStats stats;
+ asset_clear_from_idptr_list(&ids, &stats);
+ BLI_freelistN(&ids);
+
+ if (!asset_clear_result_report(&stats, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_clear(wmOperatorType *ot)
+{
+ ot->name = "Clear Asset";
+ ot->description =
+ "Delete all asset metadata and turn the selected asset data-blocks back into normal "
+ "data-blocks";
+ ot->idname = "ASSET_OT_clear";
+
+ ot->exec = asset_clear_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------------------------------------- */
+
+void ED_operatortypes_asset(void)
+{
+ WM_operatortype_append(ASSET_OT_mark);
+ WM_operatortype_append(ASSET_OT_clear);
+}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index db472c9ffa7..2b627971286 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -3838,7 +3838,7 @@ void CURVE_OT_subdivide(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of cuts", "", 1, 10);
+ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
/* Avoid re-using last var because it can cause _very_ high poly meshes
* and annoy users (or worse crash). */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 93767127cc7..4e2951c3571 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -41,6 +41,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 315b3c281da..65141442237 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index f26fd936d40..0c8cc621a3b 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 63aa242275a..09b57029350 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -41,6 +41,7 @@
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 33a1469beab..aff109eb98e 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -42,6 +42,7 @@
#include "DNA_anim_types.h"
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@@ -1330,19 +1331,24 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst);
}
- /* Read all frames from merge layer and add any missing in destination layer. */
+ /* Read all frames from merge layer and add any missing in destination layer,
+ * copying all previous strokes to keep the image equals.
+ * Need to do it in a separated loop to avoid strokes accumulation. */
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));
if (!gpf_dst) {
- gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Duplicate strokes into destination frame. */
- if (gpf_dst) {
- BKE_gpencil_frame_copy_strokes(gpf_src, gpf_dst);
- }
+ gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY);
+ BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst);
}
- else {
- /* Add to tail all strokes. */
+ }
+
+ /* Read all frames from merge layer and add strokes. */
+ 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));
+ /* Add to tail all strokes. */
+ if (gpf_dst) {
BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 95c94f8cfed..36e383cf3c2 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -41,6 +41,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 93941ea3766..39968aac9fa 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -35,6 +35,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_image_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_windowmanager_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 3617f20763e..9bca294cf30 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -283,8 +283,8 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
tgpil->gpl = gpl;
- tgpil->prevFrame = gpl->actframe;
- tgpil->nextFrame = gpl->actframe->next;
+ tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe);
+ tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next);
BLI_addtail(&tgpi->ilayers, tgpil);
@@ -326,24 +326,25 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
valid = false;
}
- /* create new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
if (valid) {
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
- new_stroke->points = MEM_recallocN(new_stroke->points,
- sizeof(*new_stroke->points) * gps_to->totpoints);
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert,
- sizeof(*new_stroke->dvert) * gps_to->totpoints);
- }
- new_stroke->totpoints = gps_to->totpoints;
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
}
- /* update points position */
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
+ }
+
+ /* Create new stroke. */
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
+ /* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
}
else {
+ /* Create new stroke. */
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
/* need an empty stroke to keep index correct for lookup, but resize to smallest size */
new_stroke->totpoints = 0;
new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
@@ -443,12 +444,16 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* finally, free memory used by temp data */
LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) {
+ BKE_gpencil_free_strokes(tgpil->prevFrame);
+ BKE_gpencil_free_strokes(tgpil->nextFrame);
BKE_gpencil_free_strokes(tgpil->interFrame);
- MEM_freeN(tgpil->interFrame);
+ MEM_SAFE_FREE(tgpil->prevFrame);
+ MEM_SAFE_FREE(tgpil->nextFrame);
+ MEM_SAFE_FREE(tgpil->interFrame);
}
BLI_freelistN(&tgpi->ilayers);
- MEM_freeN(tgpi);
+ MEM_SAFE_FREE(tgpi);
}
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
@@ -992,8 +997,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* store extremes */
- prevFrame = gpl->actframe;
- nextFrame = gpl->actframe->next;
+ prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe);
+ nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next);
/* Loop over intermediary frames and create the interpolation */
for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
@@ -1049,28 +1054,17 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
}
- /* create new stroke */
- bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
- /* free weights of removed points */
- if (new_stroke->dvert != NULL) {
- BKE_defvert_array_free_elems(new_stroke->dvert + gps_to->totpoints,
- gps_from->totpoints - gps_to->totpoints);
- }
-
- new_stroke->points = MEM_recallocN(new_stroke->points,
- sizeof(*new_stroke->points) * gps_to->totpoints);
-
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert,
- sizeof(*new_stroke->dvert) * gps_to->totpoints);
- }
-
- new_stroke->totpoints = gps_to->totpoints;
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ }
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
}
+ /* create new stroke */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
/* update points position */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
@@ -1081,6 +1075,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
BLI_addtail(&interFrame->strokes, new_stroke);
}
}
+
+ BKE_gpencil_free_strokes(prevFrame);
+ BKE_gpencil_free_strokes(nextFrame);
+ MEM_SAFE_FREE(prevFrame);
+ MEM_SAFE_FREE(nextFrame);
}
/* notifiers */
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 9f2bf3818a4..272dff56291 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -31,6 +31,7 @@
#include "BLI_math.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 4721736489e..815bbbaa254 100644
--- a/source/blender/editors/gpencil/gpencil_ops_versioning.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -33,6 +33,7 @@
#include "BLI_math.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index ed18c2eed5d..bb9dd8cac5d 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -41,6 +41,7 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 2c0b9534141..281ab8c5adc 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index e8e25a55796..c3ac33063af 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -40,7 +40,9 @@
#include "PIL_time.h"
#include "DNA_brush_types.h"
+#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index c3fd8d10b64..90b2c1c3895 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -32,6 +32,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index a4dc677f0dc..3afff897734 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -31,6 +31,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 3501acd4fdf..0c4576096fb 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -31,7 +31,6 @@ struct Base;
struct Bone;
struct Depsgraph;
struct EditBone;
-struct IDProperty;
struct ListBase;
struct Main;
struct Mesh;
diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h
new file mode 100644
index 00000000000..6fe50528cc5
--- /dev/null
+++ b/source/blender/editors/include/ED_asset.h
@@ -0,0 +1,39 @@
+/*
+ * 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 editors
+ */
+
+#ifndef __ED_ASSET_H__
+#define __ED_ASSET_H__
+
+#ifdef __cplusplus
+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);
+
+void ED_operatortypes_asset(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ED_ASSET_H__ */
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index a5a8df916d6..7b240e0569f 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -28,7 +28,9 @@ extern "C" {
#endif
struct ARegion;
+struct FileAssetSelectParams;
struct FileSelectParams;
+struct FileDirEntry;
struct Scene;
struct ScrArea;
struct SpaceFile;
@@ -103,14 +105,14 @@ struct rcti;
struct FileSelectParams *ED_fileselect_ensure_active_params(struct SpaceFile *sfile);
struct FileSelectParams *ED_fileselect_get_active_params(const struct SpaceFile *sfile);
+struct FileSelectParams *ED_fileselect_get_file_params(const struct SpaceFile *sfile);
+struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceFile *sfile);
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
const int temp_win_size[],
const bool is_maximized);
-void ED_fileselect_reset_params(struct SpaceFile *sfile);
-
void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region);
FileLayout *ED_fileselect_get_layout(struct SpaceFile *sfile, struct ARegion *region);
@@ -142,6 +144,8 @@ void ED_fileselect_exit(struct wmWindowManager *wm,
struct Scene *owner_scene,
struct SpaceFile *sfile);
+bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
+
void ED_fileselect_window_params_get(const struct wmWindow *win,
int win_size[2],
bool *is_maximized);
@@ -151,6 +155,7 @@ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win,
int ED_path_extension_type(const char *path);
int ED_file_extension_icon(const char *path);
+int ED_file_icon(const struct FileDirEntry *file);
void ED_file_read_bookmarks(void);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index be2f714dfe1..1b7caf27ecf 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -51,7 +51,6 @@ struct ScrArea;
struct SnapObjectContext;
struct ToolSettings;
struct View3D;
-struct ViewLayer;
struct bContext;
struct Material;
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index c1d3a17b9b6..b139b0765a3 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -34,12 +34,10 @@ struct ARegion;
struct ImBuf;
struct Image;
struct ImageUser;
-struct LinkNodePair;
struct Main;
struct ReportList;
struct Scene;
struct SpaceImage;
-struct ViewLayer;
struct bContext;
struct wmOperator;
struct wmWindowManager;
diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h
index e3ce494e09a..9ac6b6c1085 100644
--- a/source/blender/editors/include/ED_info.h
+++ b/source/blender/editors/include/ED_info.h
@@ -38,8 +38,12 @@ const char *ED_info_statistics_string(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);
-void ED_info_draw_stats(
- struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height);
+void ED_info_draw_stats(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ int x,
+ int *y,
+ int height);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 2e9b711c99a..f9358f62274 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -53,7 +53,6 @@ struct uiLayout;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
-struct wmWindowManager;
/* object_edit.c */
/* context.object */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index dc1c43c0337..20417634020 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -239,6 +239,7 @@ void ED_screen_restore_temp_type(struct bContext *C, ScrArea *area);
ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *area, int type);
void ED_screen_full_prevspace(struct bContext *C, ScrArea *area);
void ED_screen_full_restore(struct bContext *C, ScrArea *area);
+ScrArea *ED_screen_state_maximized_create(struct bContext *C);
struct ScrArea *ED_screen_state_toggle(struct bContext *C,
struct wmWindow *win,
struct ScrArea *area,
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 0ea86e006e0..ca3e351a052 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -32,7 +32,6 @@ extern "C" {
struct Object;
struct bContext;
struct wmKeyConfig;
-struct wmMsgBus;
struct wmOperatorType;
void ED_keymap_transform(struct wmKeyConfig *keyconf);
@@ -108,7 +107,6 @@ bool calculateTransformCenter(struct bContext *C,
struct Object;
struct Scene;
-struct wmGizmoGroup;
struct wmGizmoGroupType;
/* UNUSED */
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index ebaa32941f2..b7174964ef6 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -31,7 +31,6 @@ struct BMVert;
struct ARegion;
struct Depsgraph;
struct ListBase;
-struct Main;
struct Object;
struct Scene;
struct View3D;
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 68ae3589064..ca6b4bdc618 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -53,6 +53,8 @@ void ED_spacedata_id_remap(struct ScrArea *area,
struct ID *new_id);
void ED_OT_flush_edits(struct wmOperatorType *ot);
+void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot);
+void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot);
/* ************** XXX OLD CRUFT WARNING ************* */
diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h
index d142d3d6425..4bbaa68e849 100644
--- a/source/blender/editors/include/ED_util_imbuf.h
+++ b/source/blender/editors/include/ED_util_imbuf.h
@@ -31,7 +31,6 @@ extern "C" {
#endif
struct ARegion;
-struct Main;
struct bContext;
struct wmEvent;
struct wmOperator;
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 2066d7da511..4de97411059 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -33,7 +33,6 @@ struct BMEditMesh;
struct BMFace;
struct BMLoop;
struct BMesh;
-struct Depsgraph;
struct Image;
struct ImageUser;
struct Main;
@@ -217,8 +216,10 @@ struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l);
struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm);
-char ED_uvedit_select_mode_get(const Scene *scene);
-void ED_uvedit_select_sync_flush(const ToolSettings *ts, struct BMEditMesh *em, const bool select);
+char ED_uvedit_select_mode_get(const struct Scene *scene);
+void ED_uvedit_select_sync_flush(const struct ToolSettings *ts,
+ struct BMEditMesh *em,
+ const bool select);
/* uvedit_unwrap_ops.c */
void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit);
@@ -244,7 +245,7 @@ struct UVPackIsland_Params {
uint use_seams : 1;
uint correct_aspect : 1;
};
-void ED_uvedit_pack_islands_multi(const Scene *scene,
+void ED_uvedit_pack_islands_multi(const struct Scene *scene,
Object **objects,
const uint objects_len,
const struct UVPackIsland_Params *params);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 596533406c3..a4856845a65 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -41,8 +41,6 @@ struct Camera;
struct CustomData_MeshMasks;
struct Depsgraph;
struct EditBone;
-struct GPUOffScreen;
-struct GPUViewport;
struct ID;
struct MVert;
struct Main;
@@ -55,7 +53,6 @@ struct RenderEngineType;
struct Scene;
struct ScrArea;
struct View3D;
-struct View3DShading;
struct ViewContext;
struct ViewLayer;
struct bContext;
@@ -64,8 +61,6 @@ struct bScreen;
struct rctf;
struct rcti;
struct wmGizmo;
-struct wmOperator;
-struct wmOperatorType;
struct wmWindow;
struct wmWindowManager;
@@ -141,6 +136,11 @@ void ED_view3d_to_object(const struct Depsgraph *depsgraph,
const float quat[4],
const float dist);
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *camera_ob);
+
void ED_view3d_lastview_store(struct RegionView3D *rv3d);
/* Depth buffer */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 005dbf0e381..7c128cbf1e6 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -50,7 +50,6 @@ struct PointerRNA;
struct PropertyRNA;
struct ReportList;
struct ResultBLF;
-struct ScrArea;
struct bContext;
struct bContextStore;
struct bNode;
@@ -727,6 +726,13 @@ void UI_block_translate(uiBlock *block, int x, int y);
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 char *path,
+ int id_type,
+ int icon,
+ struct ImBuf *imb,
+ float scale);
void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr);
void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free);
void UI_but_drag_set_name(uiBut *but, const char *name);
@@ -1879,6 +1885,7 @@ uiBlock *uiLayoutGetBlock(uiLayout *layout);
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr);
+struct bContextStore *uiLayoutGetContextStore(uiLayout *layout);
void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context);
struct wmOperatorType *UI_but_operatortype_get_from_enum_menu(struct uiBut *but,
PropertyRNA **r_prop);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 4a02c6b6e88..c5c2f0e55c4 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -3352,7 +3352,7 @@ static void ui_but_free(const bContext *C, uiBut *but)
}
if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_freeN(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
}
ui_but_extra_operator_icons_free(but);
@@ -4552,6 +4552,15 @@ static uiBut *ui_def_but_rna(uiBlock *block,
UI_but_disable(but, info);
}
+ if (proptype == PROP_POINTER) {
+ /* If the button shows an ID, automatically set it as focused in context so operators can
+ * access it.*/
+ const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
+ if (pptr.data && RNA_struct_is_ID(pptr.type)) {
+ but->context = CTX_store_add(&block->contexts, "id", &pptr);
+ }
+ }
+
if (but->flag & UI_BUT_UNDO && (ui_but_is_rna_undo(but) == false)) {
but->flag &= ~UI_BUT_UNDO;
}
@@ -6089,17 +6098,42 @@ void UI_but_drag_set_id(uiBut *but, ID *id)
{
but->dragtype = WM_DRAG_ID;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)id;
}
+void UI_but_drag_set_asset(uiBut *but,
+ const char *name,
+ const char *path,
+ int id_type,
+ int icon,
+ struct ImBuf *imb,
+ float scale)
+{
+ wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset");
+
+ BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name));
+ asset_drag->path = path;
+ asset_drag->id_type = id_type;
+
+ but->dragtype = WM_DRAG_ASSET;
+ ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
+ if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ }
+ but->dragpoin = asset_drag;
+ but->dragflag |= UI_BUT_DRAGPOIN_FREE;
+ but->imb = imb;
+ but->imb_scale = scale;
+}
+
void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
{
but->dragtype = WM_DRAG_RNA;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)ptr;
@@ -6109,7 +6143,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
{
but->dragtype = WM_DRAG_PATH;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)path;
@@ -6122,7 +6156,7 @@ void UI_but_drag_set_name(uiBut *but, const char *name)
{
but->dragtype = WM_DRAG_NAME;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)name;
@@ -6140,7 +6174,7 @@ void UI_but_drag_set_image(
but->dragtype = WM_DRAG_PATH;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)path;
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 39b405a02b8..870c3a2a13f 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -38,6 +38,7 @@
#include "BKE_idprop.h"
#include "BKE_screen.h"
+#include "ED_asset.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
@@ -503,6 +504,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
uiPopupMenu *pup;
uiLayout *layout;
+ bContextStore *previous_ctx = CTX_store_get(C);
{
uiStringInfo label = {BUT_GET_LABEL, NULL};
@@ -514,6 +516,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
if (label.strinfo) {
MEM_freeN(label.strinfo);
}
+
+ if (but->context) {
+ uiLayoutContextCopy(layout, but->context);
+ CTX_store_set(C, uiLayoutGetContextStore(layout));
+ }
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
}
@@ -946,6 +953,22 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
}
+ /* If the button reprents an id, it can set the "id" context pointer. */
+ if (ED_asset_can_make_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
+ * operator poll, but that doesn't work since the operator also works with "selected_ids",
+ * which isn't cheap to check. */
+ uiLayout *sub = uiLayoutColumn(layout, true);
+ uiLayoutSetEnabled(sub, !id->asset_data);
+ uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_mark");
+ sub = uiLayoutColumn(layout, true);
+ uiLayoutSetEnabled(sub, id->asset_data);
+ uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_clear");
+ uiItemS(layout);
+ }
+
/* Pointer properties and string properties with
* prop_search support jumping to target object/bone. */
if (but->rnapoin.data && but->rnaprop) {
@@ -1210,6 +1233,10 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
}
+ if (but->context) {
+ CTX_store_set(C, previous_ctx);
+ }
+
return UI_popup_menu_end_or_cancel(C, pup);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index 7f735a0e789..f2899fc0098 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -34,6 +34,7 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index f914ccd7497..72ed2cc0933 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -626,7 +626,11 @@ static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop)
if (base == NULL) {
base = ptr->type;
}
- if (ELEM(base, &RNA_AddonPreferences, &RNA_KeyConfigPreferences, &RNA_KeyMapItem)) {
+ if (ELEM(base,
+ &RNA_AddonPreferences,
+ &RNA_KeyConfigPreferences,
+ &RNA_KeyMapItem,
+ &RNA_UserAssetLibrary)) {
tag = true;
}
}
@@ -1349,6 +1353,9 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl
if (mbut_state == NULL) {
/* Highly unlikely. */
printf("%s: Can't find button\n", __func__);
+ /* While this avoids crashing, multi-button dragging will fail,
+ * which is still a bug from the user perspective. See T83651. */
+ continue;
}
void *active_back;
@@ -1984,6 +1991,8 @@ static bool ui_but_drag_init(bContext *C,
else {
wmDrag *drag = WM_event_start_drag(
C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP);
+ /* wmDrag has ownership over dragpoin now, stop messing with it. */
+ but->dragpoin = NULL;
if (but->imb) {
WM_event_drag_image(drag,
@@ -2256,10 +2265,11 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB
ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */
LISTBASE_FOREACH (wmDrag *, wmd, drags) {
+ /* TODO asset dropping. */
if (wmd->type == WM_DRAG_ID) {
/* align these types with UI_but_active_drop_name */
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
- ID *id = WM_drag_ID(wmd, 0);
+ ID *id = WM_drag_get_local_ID(wmd, 0);
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 90f5172f6ec..899f4a6ddb1 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -100,11 +100,12 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha);
#define ICON_TYPE_COLOR_TEXTURE 1
#define ICON_TYPE_MONO_TEXTURE 2
#define ICON_TYPE_BUFFER 3
-#define ICON_TYPE_VECTOR 4
-#define ICON_TYPE_GEOM 5
-#define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */
-#define ICON_TYPE_GPLAYER 7
-#define ICON_TYPE_BLANK 8
+#define ICON_TYPE_IMBUF 4
+#define ICON_TYPE_VECTOR 5
+#define ICON_TYPE_GEOM 6
+#define ICON_TYPE_EVENT 7 /* draw keymap entries using custom renderer. */
+#define ICON_TYPE_GPLAYER 8
+#define ICON_TYPE_BLANK 9
typedef struct DrawInfo {
int type;
@@ -1147,6 +1148,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon)
if (ELEM(icon_data_type, ICON_DATA_ID, ICON_DATA_PREVIEW)) {
di->type = ICON_TYPE_PREVIEW;
}
+ else if (icon_data_type == ICON_DATA_IMBUF) {
+ di->type = ICON_TYPE_IMBUF;
+ }
else if (icon_data_type == ICON_DATA_GEOM) {
di->type = ICON_TYPE_GEOM;
}
@@ -1262,7 +1266,7 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
else if (!prv_img->rect[size]) {
prv_img->w[size] = render_size;
prv_img->h[size] = render_size;
- prv_img->flag[size] |= PRV_CHANGED;
+ prv_img->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED);
prv_img->changed_timestamp[size] = 0;
prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(uint), "prv_rect");
}
@@ -1384,8 +1388,12 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
}
}
-/* only called when icon has changed */
-/* only call with valid pointer from UI_icon_draw */
+/**
+ * * Only call with valid pointer from UI_icon_draw.
+ * * Only called when icon has changed.
+ *
+ * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored.
+ */
static void icon_set_image(const bContext *C,
Scene *scene,
ID *id,
@@ -1408,7 +1416,7 @@ static void icon_set_image(const bContext *C,
const bool delay = prv_img->rect[size] != NULL;
icon_create_rect(prv_img, size);
- if (use_job) {
+ if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) {
/* Job (background) version */
ED_preview_icon_job(
C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay);
@@ -1790,7 +1798,14 @@ static void icon_draw_size(float x,
/* We need to flush widget base first to ensure correct ordering. */
UI_widgetbase_draw_cache_flush();
- if (di->type == ICON_TYPE_VECTOR) {
+ if (di->type == ICON_TYPE_IMBUF) {
+ ImBuf *ibuf = icon->obj;
+
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
+ icon_draw_rect(x, y, w, h, aspect, ibuf->x, ibuf->y, ibuf->rect, alpha, desaturate);
+ GPU_blend(GPU_BLEND_ALPHA);
+ }
+ else if (di->type == ICON_TYPE_VECTOR) {
/* vector icons use the uiBlock transformation, they are not drawn
* with untransformed coordinates like the other icons */
di->data.vector.func((int)x, (int)y, w, h, 1.0f);
@@ -1937,6 +1952,9 @@ static void ui_id_preview_image_render_size(
}
}
+/**
+ * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored.
+ */
void UI_icon_render_id(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job)
{
PreviewImage *pi = BKE_previewimg_id_ensure(id);
@@ -1964,12 +1982,7 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs)
}
for (enum eIconSizes i = 0; i < NUM_ICON_SIZES; i++) {
- /* check if rect needs to be created; changed
- * only set by dynamic icons */
- if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) {
- icon_set_image(C, NULL, id, pi, i, use_jobs);
- pi->flag[i] &= ~PRV_CHANGED;
- }
+ ui_id_preview_image_render_size(C, NULL, id, pi, i, use_jobs);
}
}
@@ -2186,6 +2199,9 @@ int UI_icon_from_library(const ID *id)
if (ID_IS_OVERRIDE_LIBRARY(id)) {
return ICON_LIBRARY_DATA_OVERRIDE;
}
+ if (ID_IS_ASSET(id)) {
+ return ICON_MAT_SPHERE_SKY;
+ }
return ICON_NONE;
}
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 0403287125c..4b19b8f97f4 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -651,7 +651,7 @@ static void ui_item_array(uiLayout *layout,
uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y);
}
else {
- /* even if 'expand' is fale, expanding anyway */
+ /* Even if 'expand' is false, we expand anyway. */
/* layout for known array subtypes */
char str[3] = {'\0'};
@@ -5663,6 +5663,11 @@ void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *p
layout->context = CTX_store_add(&block->contexts, name, ptr);
}
+bContextStore *uiLayoutGetContextStore(uiLayout *layout)
+{
+ return layout->context;
+}
+
void uiLayoutContextCopy(uiLayout *layout, bContextStore *context)
{
uiBlock *block = layout->root->block;
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index c3d528ad5c5..a37fb0dfde1 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -206,8 +206,12 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
BLF_disable(fs->uifont_id, font_flag);
- *r_xofs = xofs;
- *r_yofs = yofs;
+ if (r_xofs) {
+ *r_xofs = xofs;
+ }
+ if (r_yofs) {
+ *r_yofs = yofs;
+ }
}
void UI_fontstyle_draw(const uiFontStyle *fs,
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 43ead511cfe..b895f1702f4 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -895,6 +895,9 @@ static void template_ID(const bContext *C,
idfrom = template_ui->ptr.owner_id;
// lb = template_ui->idlb;
+ /* Allow opertators to take the ID from context. */
+ uiLayoutSetContextPointer(layout, "id", &idptr);
+
block = uiLayoutGetBlock(layout);
UI_block_align_begin(block);
@@ -2011,7 +2014,7 @@ static void set_constraint_expand_flag(const bContext *UNUSED(C), Panel *panel,
/**
* Function with void * argument for #uiListPanelIDFromDataFunc.
*
- * \note: Constraint panel types are assumed to be named with the struct name field
+ * \note Constraint panel types are assumed to be named with the struct name field
* concatenated to the defined prefix.
*/
static void object_constraint_panel_id(void *md_link, char *r_name)
@@ -6170,6 +6173,10 @@ void uiTemplateList(uiLayout *layout,
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);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index a5999962e09..7de0ec0255f 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -109,14 +109,13 @@ typedef struct v2dViewPanData {
static bool view_pan_poll(bContext *C)
{
ARegion *region = CTX_wm_region(C);
- View2D *v2d;
/* check if there's a region in context to work with */
if (region == NULL) {
return false;
}
- v2d = &region->v2d;
+ View2D *v2d = &region->v2d;
/* check that 2d-view can pan */
if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
@@ -191,10 +190,7 @@ static void view_pan_apply(bContext *C, wmOperator *op)
/* cleanup temp customdata */
static void view_pan_exit(wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
/** \} */
@@ -216,14 +212,12 @@ static int view_pan_exec(bContext *C, wmOperator *op)
static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *window = CTX_wm_window(C);
- v2dViewPanData *vpd;
- View2D *v2d;
/* set up customdata */
view_pan_init(C, op);
- vpd = op->customdata;
- v2d = vpd->v2d;
+ v2dViewPanData *vpd = op->customdata;
+ View2D *v2d = vpd->v2d;
/* set initial settings */
vpd->startx = vpd->lastx = event->x;
@@ -787,7 +781,6 @@ static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
static bool view_zoom_poll(bContext *C)
{
ARegion *region = CTX_wm_region(C);
- View2D *v2d;
/* check if there's a region in context to work with */
if (region == NULL) {
@@ -799,7 +792,7 @@ static bool view_zoom_poll(bContext *C)
return false;
}
- v2d = &region->v2d;
+ View2D *v2d = &region->v2d;
/* check that 2d-view is zoomable */
if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
@@ -836,12 +829,12 @@ static void view_zoomstep_apply_ex(bContext *C,
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
const rctf cur_old = v2d->cur;
- float dx, dy;
const int snap_test = ED_region_snap_size_test(region);
/* calculate amount to move view by, ensuring symmetry so the
* old zoom level is restored after zooming back the same amount
*/
+ float dx, dy;
if (facx >= 0.0f) {
dx = BLI_rctf_size_x(&v2d->cur) * facx;
dy = BLI_rctf_size_y(&v2d->cur) * facy;
@@ -955,10 +948,7 @@ static void view_zoomstep_exit(wmOperator *op)
{
UI_view2d_zoom_cache_reset();
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
@@ -1107,15 +1097,14 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
{
v2dViewZoomData *vzd = op->customdata;
View2D *v2d = vzd->v2d;
- float dx, dy;
const int snap_test = ED_region_snap_size_test(vzd->region);
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
/* get amount to move view by */
- dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
- dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
+ float dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
+ float dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
if (U.uiflag & USER_ZOOM_INVERT) {
dx *= -1;
@@ -1223,14 +1212,12 @@ static int view_zoomdrag_exec(bContext *C, wmOperator *op)
static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *window = CTX_wm_window(C);
- v2dViewZoomData *vzd;
- View2D *v2d;
/* set up customdata */
view_zoomdrag_init(C, op);
- vzd = op->customdata;
- v2d = vzd->v2d;
+ v2dViewZoomData *vzd = op->customdata;
+ View2D *v2d = vzd->v2d;
if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
ARegion *region = CTX_wm_region(C);
@@ -1242,20 +1229,18 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even
}
if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
- float dx, dy, fac;
-
vzd->lastx = event->prevx;
vzd->lasty = event->prevy;
/* As we have only 1D information (magnify value), feed both axes
* with magnify information that is stored in x axis
*/
- fac = 0.01f * (event->prevx - event->x);
- dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f;
+ float fac = 0.01f * (event->prevx - event->x);
+ float dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f;
if (event->type == MOUSEPAN) {
fac = 0.01f * (event->prevy - event->y);
}
- dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f;
+ float dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f;
/* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
* landscape exceptions */
@@ -1491,11 +1476,11 @@ static int view_borderzoom_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- rctf rect;
rctf cur_new = v2d->cur;
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
/* convert coordinates of rect to 'tot' rect coordinates */
+ rctf rect;
WM_operator_properties_border_to_rctf(op, &rect);
UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
@@ -1766,13 +1751,13 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
struct SmoothView2DStore *sms = v2d->sms;
- float step;
/* escape if not our timer */
if (v2d->smooth_timer == NULL || v2d->smooth_timer != event->customdata) {
return OPERATOR_PASS_THROUGH;
}
+ float step;
if (sms->time_allowed != 0.0) {
step = (float)((v2d->smooth_timer->duration) / sms->time_allowed);
}
@@ -1978,15 +1963,11 @@ static void scroller_activate_init(bContext *C,
const wmEvent *event,
const char in_scroller)
{
- v2dScrollerMove *vsm;
- View2DScrollers scrollers;
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- rctf tot_cur_union;
- float mask_size;
/* set custom-data for operator */
- vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
+ v2dScrollerMove *vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
op->customdata = vsm;
/* set general data */
@@ -2000,16 +1981,17 @@ static void scroller_activate_init(bContext *C,
/* 'zone' depends on where mouse is relative to bubble
* - zooming must be allowed on this axis, otherwise, default to pan
*/
+ View2DScrollers scrollers;
UI_view2d_scrollers_calc(v2d, NULL, &scrollers);
/* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
* moving the scroll bars has far too little effect and the view can get stuck T31476. */
- tot_cur_union = v2d->tot;
+ rctf tot_cur_union = v2d->tot;
BLI_rctf_union(&tot_cur_union, &v2d->cur);
if (in_scroller == 'h') {
/* horizontal scroller - calculate adjustment factor first */
- mask_size = (float)BLI_rcti_size_x(&v2d->hor);
+ float mask_size = (float)BLI_rcti_size_x(&v2d->hor);
vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
/* pixel rounding */
@@ -2029,7 +2011,7 @@ static void scroller_activate_init(bContext *C,
}
else {
/* vertical scroller - calculate adjustment factor first */
- mask_size = (float)BLI_rcti_size_y(&v2d->vert);
+ float mask_size = (float)BLI_rcti_size_y(&v2d->vert);
vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
/* pixel rounding */
@@ -2076,10 +2058,9 @@ static void scroller_activate_apply(bContext *C, wmOperator *op)
{
v2dScrollerMove *vsm = op->customdata;
View2D *v2d = vsm->v2d;
- float temp;
/* calculate amount to move view by */
- temp = vsm->fac * vsm->delta;
+ float temp = vsm->fac * vsm->delta;
/* round to pixel */
temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
@@ -2219,11 +2200,9 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *
/* if in a scroller, init customdata then set modal handler which will
* catch mouse-down to start doing useful stuff */
if (in_scroller) {
- v2dScrollerMove *vsm;
-
/* initialize customdata */
scroller_activate_init(C, op, event, in_scroller);
- vsm = (v2dScrollerMove *)op->customdata;
+ v2dScrollerMove *vsm = (v2dScrollerMove *)op->customdata;
/* support for quick jump to location - gtk and qt do this on linux */
if (event->type == MIDDLEMOUSE) {
@@ -2324,12 +2303,11 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op))
const uiStyle *style = UI_style_get();
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- int winx, winy;
const int snap_test = ED_region_snap_size_test(region);
/* zoom 1.0 */
- winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
- winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
+ const int winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
+ const int winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
v2d->cur.xmax = v2d->cur.xmin + winx;
v2d->cur.ymax = v2d->cur.ymin + winy;
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index b8badd207fe..e788b28d3b4 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -1129,7 +1129,7 @@ void MESH_OT_bevel(wmOperatorType *ot)
prop_affect_items,
BEVEL_AFFECT_EDGES,
"Affect",
- "Affect Edges or Vertices");
+ "Affect edges or vertices");
RNA_def_boolean(ot->srna,
"clamp_overlap",
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index ef78d31a6bb..aa144dd3f3c 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -177,6 +177,6 @@ void MESH_OT_knife_project(wmOperatorType *ot)
RNA_def_boolean(ot->srna,
"cut_through",
false,
- "Cut through",
+ "Cut Through",
"Cut through all faces, not just visible ones");
}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index a2fe949b6c5..d3eaa9048d3 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -4552,7 +4552,8 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot)
/* edges */
RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges");
RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges");
- RNA_def_boolean(ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by 3+ faces");
+ RNA_def_boolean(
+ ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces");
RNA_def_boolean(ot->srna,
"use_non_contiguous",
true,
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index 00349983c57..d762eede079 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -34,6 +34,7 @@
#include "BKE_material.h"
#include "BKE_report.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "WM_api.h"
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index b961f81e16a..cf01170dd8a 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -180,7 +180,7 @@ void MESH_OT_subdivide(wmOperatorType *ot)
"ngon",
true,
"Create N-Gons",
- "When disabled, newly created faces are limited to 3-4 sided faces");
+ "When disabled, newly created faces are limited to 3 and 4 sided faces");
RNA_def_enum(
ot->srna,
"quadcorner",
@@ -395,7 +395,7 @@ void MESH_OT_unsubdivide(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Un-Subdivide";
- ot->description = "UnSubdivide selected edges & faces";
+ ot->description = "Un-subdivide selected edges and faces";
ot->idname = "MESH_OT_unsubdivide";
/* api callbacks */
@@ -698,7 +698,7 @@ void MESH_OT_edge_collapse(wmOperatorType *ot)
/* identifiers */
ot->name = "Collapse Edges & Faces";
ot->description =
- "Collapse isolated edges & faces regions, merging data such as UV's and vertex colors. "
+ "Collapse isolated edge and face regions, merging data such as UV's and vertex colors. "
"This can collapse edge-rings as well as regions of connected faces into vertices";
ot->idname = "MESH_OT_edge_collapse";
@@ -1904,7 +1904,7 @@ void MESH_OT_edge_split(wmOperatorType *ot)
"VERT",
0,
"Faces & Edges by Vertices",
- "Split faces & edges connected to selected vertices"},
+ "Split faces and edges connected to selected vertices"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 43cad2db185..cff5414da75 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -27,6 +27,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 6467df0e87b..94f386e08d5 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1139,8 +1139,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em,
if (use_topology) {
v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
- if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
- v_mirr = NULL;
+ if (v_mirr != NULL) {
+ if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
+ v_mirr = NULL;
+ }
}
}
else {
diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c
index 552e459acb1..b4030ad269b 100644
--- a/source/blender/editors/metaball/editmball_undo.c
+++ b/source/blender/editors/metaball/editmball_undo.c
@@ -30,8 +30,10 @@
#include "BLI_utildefines.h"
#include "DNA_defs.h"
+#include "DNA_layer_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_layer.h"
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index a1be9b9df61..77b5379ddd4 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -31,8 +31,8 @@ set(INC
../../makesrna
../../modifiers
../../python
- ../../shader_fx
../../render
+ ../../shader_fx
../../windowmanager
../../../../intern/clog
../../../../intern/glew-mx
@@ -88,6 +88,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index bbfdfb2532d..a64033bc63a 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1714,6 +1714,9 @@ void OBJECT_OT_hair_add(wmOperatorType *ot)
static bool object_pointcloud_add_poll(bContext *C)
{
+ if (!U.experimental.use_new_point_cloud_type) {
+ return false;
+ }
return ED_operator_objectmode(C);
}
@@ -2316,17 +2319,23 @@ static const EnumPropertyItem convert_target_items[] = {
"MESH",
ICON_OUTLINER_OB_MESH,
"Mesh",
+#ifdef WITH_POINT_CLOUD
"Mesh from Curve, Surface, Metaball, Text, or Pointcloud objects"},
+#else
+ "Mesh from Curve, Surface, Metaball, or Text objects"},
+#endif
{OB_GPENCIL,
"GPENCIL",
ICON_OUTLINER_OB_GREASEPENCIL,
"Grease Pencil",
"Grease Pencil from Curve or Mesh objects"},
+#ifdef WITH_POINT_CLOUD
{OB_POINTCLOUD,
"POINTCLOUD",
ICON_OUTLINER_OB_POINTCLOUD,
"Pointcloud",
"Pointcloud from Mesh objects"},
+#endif
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index 008498a1735..9618774eea8 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -25,6 +25,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index af95f5581bd..e5feb74df26 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -461,7 +461,7 @@ static bool gpencil_edit_modifier_poll(bContext *C)
* (not only from added 'local' ones). */
static bool gpencil_edit_modifier_liboverride_allowed_poll(bContext *C)
{
- return gpencil_edit_modifier_poll_generic(C, &RNA_Modifier, 0, true);
+ return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0, true);
}
static void gpencil_edit_modifier_properties(wmOperatorType *ot)
@@ -789,7 +789,7 @@ void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot)
"apply_as",
gpencil_modifier_apply_as_items,
MODIFIER_APPLY_DATA,
- "Apply as",
+ "Apply As",
"How to apply the modifier to the geometry");
gpencil_edit_modifier_properties(ot);
gpencil_edit_modifier_report_property(ot);
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index e6ef53a3d65..89ade5cc49d 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -206,6 +206,7 @@ void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot);
/* object_shader_fx.c */
void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot);
+void OBJECT_OT_shaderfx_copy(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 8ba0ce5fd08..2e5a75ffa7d 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -166,6 +166,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_shaderfx_move_up);
WM_operatortype_append(OBJECT_OT_shaderfx_move_down);
WM_operatortype_append(OBJECT_OT_shaderfx_move_to_index);
+ WM_operatortype_append(OBJECT_OT_shaderfx_copy);
WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 8841b1955bf..5caa7c71e83 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1554,6 +1554,7 @@ enum {
MAKE_LINKS_DUPLICOLLECTION = 5,
MAKE_LINKS_MODIFIERS = 6,
MAKE_LINKS_FONTS = 7,
+ MAKE_LINKS_SHADERFX = 8,
};
/* Return true if make link data is allowed, false otherwise */
@@ -1589,6 +1590,11 @@ static bool allow_make_links_data(const int type, Object *ob_src, Object *ob_dst
return true;
}
break;
+ case MAKE_LINKS_SHADERFX:
+ if ((ob_src->type == OB_GPENCIL) && (ob_dst->type == OB_GPENCIL)) {
+ return true;
+ }
+ break;
}
return false;
}
@@ -1720,6 +1726,11 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
break;
}
+ case MAKE_LINKS_SHADERFX:
+ ED_object_shaderfx_link(ob_dst, ob_src);
+ DEG_id_tag_update(&ob_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
+ break;
}
}
}
@@ -1782,6 +1793,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot)
{MAKE_LINKS_DUPLICOLLECTION, "DUPLICOLLECTION", 0, "Instance Collection", ""},
{MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Modifiers", ""},
{MAKE_LINKS_FONTS, "FONTS", 0, "Fonts", ""},
+ {MAKE_LINKS_SHADERFX, "EFFECTS", 0, "Effects", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2515,7 +2527,7 @@ static bool convert_proxy_to_override_poll(bContext *C)
return obact != NULL && obact->proxy != NULL;
}
-static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
+static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -2529,6 +2541,15 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy);
+ if (!success) {
+ BKE_reportf(
+ op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not create a library override from proxy '%s' (might use already local data?)",
+ ob_proxy->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (success && is_override_instancing_object) {
@@ -2544,7 +2565,7 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Convert Proxy To Override";
+ ot->name = "Convert Proxy to Override";
ot->description = "Convert a proxy to a local library override";
ot->idname = "OBJECT_OT_convert_proxy_to_override";
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index c5caee5ba08..2b1ac08ec2e 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -640,3 +640,57 @@ void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
RNA_def_int(
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX);
}
+
+/************************ copy shader operator *********************/
+
+static int shaderfx_copy_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+
+ ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
+ if (!nfx) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name));
+ /* Make sure effect data has unique name. */
+ BKE_shaderfx_unique_name(&ob->shader_fx, nfx);
+
+ BKE_shaderfx_copydata(fx, nfx);
+ BLI_insertlinkafter(&ob->shader_fx, fx, nfx);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int shaderfx_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
+ return shaderfx_copy_exec(C, op);
+ }
+ return retval;
+}
+
+static bool shaderfx_copy_poll(bContext *C)
+{
+ return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0);
+}
+
+void OBJECT_OT_shaderfx_copy(wmOperatorType *ot)
+{
+ ot->name = "Copy Effect";
+ ot->description = "Duplicate effect at the same position in the stack";
+ ot->idname = "OBJECT_OT_shaderfx_copy";
+
+ ot->invoke = shaderfx_copy_invoke;
+ ot->exec = shaderfx_copy_exec;
+ ot->poll = shaderfx_copy_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ edit_shaderfx_properties(ot);
+}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 3d6a6abfe0d..23f1718cb2e 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -741,6 +741,9 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *
RNA_enum_items_add_value(
&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_SELECT);
}
+ }
+
+ if (BKE_modifiers_is_deformed_by_armature(ob)) {
if (selection_mask & (1 << WT_VGROUP_BONE_DEFORM)) {
RNA_enum_items_add_value(
&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_DEFORM);
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 017cd63d9d5..f5c3fc17552 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -1203,6 +1203,9 @@ static bool copy_particle_systems_to_object(const bContext *C,
#undef PSYS_FROM_FIRST
#undef PSYS_FROM_NEXT
+ if (duplicate_settings) {
+ DEG_relations_tag_update(bmain);
+ }
DEG_id_tag_update(&ob_to->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to);
return true;
diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
index 4939bf0086b..cb7ca5bd5d1 100644
--- a/source/blender/editors/physics/rigidbody_constraint.c
+++ b/source/blender/editors/physics/rigidbody_constraint.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index cb25363d2b2..4fd304ea71d 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 3dbf70aa4bc..a035ee3e342 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -150,31 +150,31 @@ static void image_buffer_rect_update(RenderJob *rj,
}
/* xmin here is first subrect x coord, xmax defines subrect width */
- xmin = renrect->xmin + rr->crop;
- xmax = renrect->xmax - xmin + rr->crop;
+ xmin = renrect->xmin;
+ xmax = renrect->xmax - xmin;
if (xmax < 2) {
return;
}
- ymin = renrect->ymin + rr->crop;
- ymax = renrect->ymax - ymin + rr->crop;
+ ymin = renrect->ymin;
+ ymax = renrect->ymax - ymin;
if (ymax < 2) {
return;
}
renrect->ymin = renrect->ymax;
}
else {
- xmin = ymin = rr->crop;
- xmax = rr->rectx - 2 * rr->crop;
- ymax = rr->recty - 2 * rr->crop;
+ xmin = ymin = 0;
+ xmax = rr->rectx;
+ ymax = rr->recty;
}
/* xmin ymin is in tile coords. transform to ibuf */
- rxmin = rr->tilerect.xmin + xmin;
+ rxmin = rr->tilerect.xmin;
if (rxmin >= ibuf->x) {
return;
}
- rymin = rr->tilerect.ymin + ymin;
+ rymin = rr->tilerect.ymin;
if (rymin >= ibuf->y) {
return;
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 095deccada0..579fd86077e 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -68,6 +68,7 @@
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -94,12 +95,16 @@
#include "ED_datafiles.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_view3d.h"
+#include "ED_view3d_offscreen.h"
#ifndef NDEBUG
/* Used for database init assert(). */
# include "BLI_threads.h"
#endif
+static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect);
+
ImBuf *get_brush_icon(Brush *brush)
{
static const int flags = IB_rect | IB_multilayer | IB_metadata;
@@ -184,7 +189,7 @@ typedef struct IconPreview {
Main *bmain;
Scene *scene;
void *owner;
- ID *id, *id_copy;
+ ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */
ListBase sizes;
} IconPreview;
@@ -336,7 +341,7 @@ static World *preview_get_localized_world(ShaderPreview *sp, World *world)
return sp->worldcopy;
}
-static ID *duplicate_ids(ID *id)
+static ID *duplicate_ids(ID *id, const bool allow_failure)
{
if (id == NULL) {
/* Non-ID preview render. */
@@ -344,20 +349,25 @@ static ID *duplicate_ids(ID *id)
}
switch (GS(id->name)) {
+ case ID_OB:
case ID_MA:
case ID_TE:
case ID_LA:
case ID_WO: {
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
ID *id_copy = BKE_id_copy_ex(
NULL, id, NULL, LIB_ID_CREATE_LOCAL | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA);
return id_copy;
}
+ /* These support threading, but don't need duplicating. */
case ID_IM:
case ID_BR:
- case ID_SCR:
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
return NULL;
default:
- BLI_assert(!"ID type preview not supported.");
+ if (!allow_failure) {
+ BLI_assert(!"ID type preview not supported.");
+ }
return NULL;
}
}
@@ -698,6 +708,132 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r
}
}
+/* **************************** Object preview ****************** */
+
+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). */
+ Object *object;
+ int sizex;
+ int sizey;
+};
+
+static Object *object_preview_camera_create(
+ Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+{
+ Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
+
+ float rotmat[3][3];
+ float dummyscale[3];
+ mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat);
+
+ /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */
+ float drotmat[3][3];
+ axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ mul_m3_m3_post(rotmat, drotmat);
+
+ camera->rotmode = ROT_MODE_QUAT;
+ mat3_to_quat(camera->quat, rotmat);
+
+ /* shader_preview_render() does this too. */
+ if (sizex > sizey) {
+ ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex;
+ }
+
+ return camera;
+}
+
+static Scene *object_preview_scene_create(const struct ObjectPreviewData *preview_data,
+ Depsgraph **r_depsgraph)
+{
+ Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
+ ViewLayer *view_layer = scene->view_layers.first;
+ Depsgraph *depsgraph = DEG_graph_new(
+ preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT);
+
+ BLI_assert(preview_data->object != NULL);
+ BLI_addtail(&preview_data->pr_main->objects, preview_data->object);
+
+ BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object);
+
+ Object *camera_object = object_preview_camera_create(preview_data->pr_main,
+ view_layer,
+ preview_data->object,
+ preview_data->sizex,
+ preview_data->sizey);
+
+ scene->camera = camera_object;
+ scene->r.xsch = preview_data->sizex;
+ scene->r.ysch = preview_data->sizey;
+ scene->r.size = 100;
+
+ Base *preview_base = BKE_view_layer_base_find(view_layer, preview_data->object);
+ /* For 'view selected' below. */
+ preview_base->flag |= BASE_SELECTED;
+
+ DEG_graph_build_from_view_layer(depsgraph);
+ DEG_evaluate_on_refresh(depsgraph);
+
+ ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object);
+
+ BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main);
+
+ *r_depsgraph = depsgraph;
+ return scene;
+}
+
+static void object_preview_render(IconPreview *preview, IconPreviewSize *preview_sized)
+{
+ Main *preview_main = BKE_main_new();
+ const float pixelsize_old = U.pixelsize;
+ char err_out[256] = "unknown";
+
+ BLI_assert(preview->id_copy && (preview->id_copy != preview->id));
+
+ struct ObjectPreviewData preview_data = {
+ .pr_main = preview_main,
+ /* Act on a copy. */
+ .object = (Object *)preview->id_copy,
+ .sizex = preview_sized->sizex,
+ .sizey = preview_sized->sizey,
+ };
+ Depsgraph *depsgraph;
+ Scene *scene = object_preview_scene_create(&preview_data, &depsgraph);
+
+ /* Ownership is now ours. */
+ preview->id_copy = NULL;
+
+ U.pixelsize = 2.0f;
+
+ ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
+ depsgraph,
+ DEG_get_evaluated_scene(depsgraph),
+ NULL,
+ OB_SOLID,
+ DEG_get_evaluated_object(depsgraph, scene->camera),
+ preview_sized->sizex,
+ preview_sized->sizey,
+ IB_rect,
+ V3D_OFSDRAW_NONE,
+ R_ALPHAPREMUL,
+ NULL,
+ NULL,
+ err_out);
+ /* TODO color-management? */
+
+ U.pixelsize = pixelsize_old;
+
+ if (ibuf) {
+ icon_copy_rect(ibuf, preview_sized->sizex, preview_sized->sizey, preview_sized->rect);
+ IMB_freeImBuf(ibuf);
+ }
+
+ DEG_graph_free(depsgraph);
+ BKE_main_free(preview_main);
+}
+
/* **************************** new shader preview system ****************** */
/* inside thread, called by renderer, sets job update value */
@@ -1101,6 +1237,8 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat
ID *id = sp->id;
short idtype = GS(id->name);
+ BLI_assert(id != NULL);
+
if (idtype == ID_IM) {
Image *ima = (Image *)id;
ImBuf *ibuf = NULL;
@@ -1188,27 +1326,72 @@ static void common_preview_startjob(void *customdata,
}
}
-/* exported functions */
-
-static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+/**
+ * Some ID types already have their own, more focused rendering (only objects right now). This is
+ * for the other ones, which all share #ShaderPreview and some functions.
+ */
+static void other_id_types_preview_render(IconPreview *ip,
+ IconPreviewSize *cur_size,
+ const bool is_deferred,
+ short *stop,
+ short *do_update,
+ float *progress)
{
- IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+ ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
+ const bool is_render = !is_deferred;
+
+ /* These types don't use the ShaderPreview mess, they have their own types and functions. */
+ BLI_assert(!ip->id || !ELEM(GS(ip->id->name), ID_OB));
+
+ /* construct shader preview from image size and previewcustomdata */
+ sp->scene = ip->scene;
+ sp->owner = ip->owner;
+ sp->sizex = cur_size->sizex;
+ sp->sizey = cur_size->sizey;
+ sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
+ sp->pr_rect = cur_size->rect;
+ sp->id = ip->id;
+ sp->id_copy = ip->id_copy;
+ sp->bmain = ip->bmain;
+ sp->own_id_copy = false;
+ Material *ma = NULL;
- while (cur_size) {
- if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
- /* requested size is already in list, no need to add it again */
- return;
+ if (is_render) {
+ BLI_assert(ip->id);
+
+ /* grease pencil use its own preview file */
+ if (GS(ip->id->name) == ID_MA) {
+ ma = (Material *)ip->id;
}
- cur_size = cur_size->next;
+ if ((ma == NULL) || (ma->gp_style == NULL)) {
+ sp->pr_main = G_pr_main;
+ }
+ else {
+ sp->pr_main = G_pr_main_grease_pencil;
+ }
}
- new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
- new_size->sizex = sizex;
- new_size->sizey = sizey;
- new_size->rect = rect;
+ common_preview_startjob(sp, stop, do_update, progress);
+ shader_preview_free(sp);
+}
- BLI_addtail(&ip->sizes, new_size);
+/* exported functions */
+
+/**
+ * Find the index to map \a icon_size to data in \a preview_image.
+ */
+static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size,
+ const PreviewImage *preview_image)
+{
+ for (int i = 0; i < NUM_ICON_SIZES; i++) {
+ if ((preview_image->w[i] == icon_size->sizex) && (preview_image->h[i] == icon_size->sizey)) {
+ return i;
+ }
+ }
+
+ BLI_assert(!"The searched icon size does not match any in the preview image");
+ return -1;
}
static void icon_preview_startjob_all_sizes(void *customdata,
@@ -1235,41 +1418,43 @@ static void icon_preview_startjob_all_sizes(void *customdata,
continue;
}
- ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
- const bool is_render = !(prv->tag & PRV_TAG_DEFFERED);
-
- /* construct shader preview from image size and previewcustomdata */
- sp->scene = ip->scene;
- sp->owner = ip->owner;
- sp->sizex = cur_size->sizex;
- sp->sizey = cur_size->sizey;
- sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
- sp->pr_rect = cur_size->rect;
- sp->id = ip->id;
- sp->id_copy = ip->id_copy;
- sp->bmain = ip->bmain;
- sp->own_id_copy = false;
- Material *ma = NULL;
-
- if (is_render) {
- BLI_assert(ip->id);
-
- /* grease pencil use its own preview file */
- if (GS(ip->id->name) == ID_MA) {
- ma = (Material *)ip->id;
- }
+#ifndef NDEBUG
+ {
+ int size_index = icon_previewimg_size_index_get(cur_size, prv);
+ BLI_assert(!BKE_previewimg_is_finished(prv, size_index));
+ }
+#endif
- if ((ma == NULL) || (ma->gp_style == NULL)) {
- sp->pr_main = G_pr_main;
- }
- else {
- sp->pr_main = G_pr_main_grease_pencil;
- }
+ if (ip->id && ELEM(GS(ip->id->name), ID_OB)) {
+ /* Much simpler than the ShaderPreview mess used for other ID types. */
+ object_preview_render(ip, cur_size);
+ }
+ else {
+ other_id_types_preview_render(
+ ip, cur_size, (prv->tag & PRV_TAG_DEFFERED), stop, do_update, progress);
+ }
+ }
+}
+
+static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+{
+ IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+
+ while (cur_size) {
+ if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
+ /* requested size is already in list, no need to add it again */
+ return;
}
- common_preview_startjob(sp, stop, do_update, progress);
- shader_preview_free(sp);
+ cur_size = cur_size->next;
}
+
+ new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
+ new_size->sizex = sizex;
+ new_size->sizey = sizey;
+ new_size->rect = rect;
+
+ BLI_addtail(&ip->sizes, new_size);
}
static void icon_preview_endjob(void *customdata)
@@ -1302,9 +1487,15 @@ static void icon_preview_endjob(void *customdata)
if (ip->owner) {
PreviewImage *prv_img = ip->owner;
prv_img->tag &= ~PRV_TAG_DEFFERED_RENDERING;
+
+ LISTBASE_FOREACH (IconPreviewSize *, icon_size, &ip->sizes) {
+ int size_index = icon_previewimg_size_index_get(icon_size, prv_img);
+ BKE_previewimg_finish(prv_img, size_index);
+ }
+
if (prv_img->tag & PRV_TAG_DEFFERED_DELETE) {
BLI_assert(prv_img->tag & PRV_TAG_DEFFERED);
- BKE_previewimg_cached_release_pointer(prv_img);
+ BKE_previewimg_deferred_release(prv_img);
}
}
}
@@ -1333,7 +1524,9 @@ void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, uint *rect, int s
ip.scene = scene;
ip.owner = BKE_previewimg_id_ensure(id);
ip.id = id;
- ip.id_copy = duplicate_ids(id);
+ /* 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);
icon_preview_add_size(&ip, rect, sizex, sizey);
@@ -1376,7 +1569,7 @@ void ED_preview_icon_job(
ip->scene = CTX_data_scene(C);
ip->owner = owner;
ip->id = id;
- ip->id_copy = duplicate_ids(id);
+ ip->id_copy = duplicate_ids(id, false);
icon_preview_add_size(ip, rect, sizex, sizey);
@@ -1416,6 +1609,8 @@ void ED_preview_shader_job(const bContext *C,
Scene *scene = CTX_data_scene(C);
short id_type = GS(id->name);
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
+
/* Use workspace render only for buttons Window,
* since the other previews are related to the datablock. */
@@ -1445,7 +1640,7 @@ void ED_preview_shader_job(const bContext *C,
sp->sizey = sizey;
sp->pr_method = method;
sp->id = id;
- sp->id_copy = duplicate_ids(id);
+ sp->id_copy = duplicate_ids(id, false);
sp->own_id_copy = true;
sp->parent = parent;
sp->slot = slot;
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 244ebea5bbe..fb7606d1fe5 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -649,8 +649,6 @@ static eContextResult screen_ctx_selected_editable_sequences(const bContext *C,
}
static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContextDataResult *result)
{
- wmWindow *win = CTX_wm_window(C);
- Scene *scene = WM_window_get_active_scene(win);
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) != 0) {
ListBase anim_data = {NULL, NULL};
@@ -663,7 +661,7 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext
NlaTrack *nlt = (NlaTrack *)ale->data;
LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
- CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip);
+ CTX_data_list_add(result, ale->id, &RNA_NlaStrip, strip);
}
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 6be2fb8004b..be52874ed0b 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1133,12 +1133,11 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene)
ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type)
{
- wmWindow *win = CTX_wm_window(C);
ScrArea *newsa = NULL;
SpaceLink *newsl;
if (!area || area->full == NULL) {
- newsa = ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED);
+ newsa = ED_screen_state_maximized_create(C);
}
if (!newsa) {
@@ -1149,11 +1148,11 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type)
newsl = newsa->spacedata.first;
/* Tag the active space before changing, so we can identify it when user wants to go back. */
- if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
+ if (newsl && (newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE;
}
- ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY);
+ ED_area_newspace(C, newsa, type, (newsl && newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY));
return newsa;
}
@@ -1217,13 +1216,108 @@ void ED_screen_full_restore(bContext *C, ScrArea *area)
}
/**
- * this function toggles: if area is maximized/full then the parent will be restored
+ * \param toggle_area: If this is set, its space data will be swapped with the one of the new emtpy
+ * area, when toggling back it can be swapped back again.
+ * \return The newly created screen with the non-normal area.
+ */
+static bScreen *screen_state_to_nonnormal(bContext *C,
+ wmWindow *win,
+ ScrArea *toggle_area,
+ int state)
+{
+ Main *bmain = CTX_data_main(C);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+
+ /* change from SCREENNORMAL to new state */
+ WorkSpaceLayout *layout_new;
+ ScrArea *newa;
+ char newname[MAX_ID_NAME - 2];
+
+ BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL));
+
+ bScreen *oldscreen = WM_window_get_active_screen(win);
+
+ oldscreen->state = state;
+ BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal");
+
+ layout_new = ED_workspace_layout_add(bmain, workspace, win, newname);
+
+ bScreen *screen = BKE_workspace_layout_screen_get(layout_new);
+ screen->state = state;
+ screen->redraws_flag = oldscreen->redraws_flag;
+ screen->temp = oldscreen->temp;
+ screen->flag = oldscreen->flag;
+
+ /* timer */
+ screen->animtimer = oldscreen->animtimer;
+ oldscreen->animtimer = NULL;
+
+ newa = (ScrArea *)screen->areabase.first;
+
+ /* swap area */
+ if (toggle_area) {
+ ED_area_data_swap(newa, toggle_area);
+ newa->flag = toggle_area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */
+ }
+
+ if (state == SCREENFULL) {
+ /* temporarily hide global areas */
+ LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
+ glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
+ }
+ /* temporarily hide the side panels/header */
+ LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
+ region->flagfullscreen = region->flag;
+
+ if (ELEM(region->regiontype,
+ RGN_TYPE_UI,
+ RGN_TYPE_HEADER,
+ RGN_TYPE_TOOL_HEADER,
+ RGN_TYPE_FOOTER,
+ RGN_TYPE_TOOLS,
+ RGN_TYPE_NAV_BAR,
+ RGN_TYPE_EXECUTE)) {
+ region->flag |= RGN_FLAG_HIDDEN;
+ }
+ }
+ }
+
+ if (toggle_area) {
+ toggle_area->full = oldscreen;
+ }
+ newa->full = oldscreen;
+
+ ED_screen_change(C, screen);
+ ED_area_tag_refresh(newa);
+
+ return screen;
+}
+
+/**
+ * Create a new temporary screen with a maximized, empty area.
+ * This can be closed with #ED_screen_state_toggle().
+ *
+ * Use this to just create a new maximized screen/area, rather than maximizing an existing one.
+ * Otherwise, maximize with #ED_screen_state_toggle().
+ */
+ScrArea *ED_screen_state_maximized_create(bContext *C)
+{
+ bScreen *screen = screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED);
+ return screen->areabase.first;
+}
+
+/**
+ * This function toggles: if area is maximized/full then the parent will be restored.
+ *
+ * Use #ED_screen_state_maximized_create() if you do not want the toggle behavior when changing to
+ * a maximized area. I.e. if you just want to open a new maximized screen/area, not maximize a
+ * specific area. In the former case, space data of the maximized and non-maximized area should be
+ * independent, in the latter it should be the same.
*
* \warning \a area may be freed.
*/
ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const short state)
{
- Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
WorkSpace *workspace = WM_window_get_active_workspace(win);
@@ -1257,7 +1351,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
screen->state = SCREENNORMAL;
screen->flag = oldscreen->flag;
- /* find old area to restore from */
+ /* Find old area we may have swapped dummy space data to. It's swapped back here. */
ScrArea *fullsa = NULL;
LISTBASE_FOREACH (ScrArea *, old, &screen->areabase) {
/* area to restore from is always first */
@@ -1271,13 +1365,6 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
area->full = NULL;
- if (fullsa == NULL) {
- if (G.debug & G_DEBUG) {
- printf("%s: something wrong in areafullscreen\n", __func__);
- }
- return NULL;
- }
-
if (state == SCREENFULL) {
/* unhide global areas */
LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
@@ -1289,14 +1376,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
}
}
- ED_area_data_swap(fullsa, area);
+ if (fullsa) {
+ ED_area_data_swap(fullsa, area);
+ ED_area_tag_refresh(fullsa);
+ }
/* animtimer back */
screen->animtimer = oldscreen->animtimer;
oldscreen->animtimer = NULL;
ED_screen_change(C, screen);
- ED_area_tag_refresh(fullsa);
BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old);
@@ -1307,68 +1396,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
screen->skip_handling = true;
}
else {
- /* change from SCREENNORMAL to new state */
- WorkSpaceLayout *layout_new;
- ScrArea *newa;
- char newname[MAX_ID_NAME - 2];
-
- BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL));
-
- bScreen *oldscreen = WM_window_get_active_screen(win);
-
- oldscreen->state = state;
- BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal");
-
- layout_new = ED_workspace_layout_add(bmain, workspace, win, newname);
-
- screen = BKE_workspace_layout_screen_get(layout_new);
- screen->state = state;
- screen->redraws_flag = oldscreen->redraws_flag;
- screen->temp = oldscreen->temp;
- screen->flag = oldscreen->flag;
-
- /* timer */
- screen->animtimer = oldscreen->animtimer;
- oldscreen->animtimer = NULL;
+ ScrArea *toggle_area = area;
/* use random area when we have no active one, e.g. when the
* mouse is outside of the window and we open a file browser */
- if (!area || area->global) {
- area = oldscreen->areabase.first;
+ if (!toggle_area || toggle_area->global) {
+ bScreen *oldscreen = WM_window_get_active_screen(win);
+ toggle_area = oldscreen->areabase.first;
}
- newa = (ScrArea *)screen->areabase.first;
-
- /* copy area */
- ED_area_data_swap(newa, area);
- newa->flag = area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */
-
- if (state == SCREENFULL) {
- /* temporarily hide global areas */
- LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
- glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
- }
- /* temporarily hide the side panels/header */
- LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
- region->flagfullscreen = region->flag;
-
- if (ELEM(region->regiontype,
- RGN_TYPE_UI,
- RGN_TYPE_HEADER,
- RGN_TYPE_TOOL_HEADER,
- RGN_TYPE_FOOTER,
- RGN_TYPE_TOOLS,
- RGN_TYPE_NAV_BAR,
- RGN_TYPE_EXECUTE)) {
- region->flag |= RGN_FLAG_HIDDEN;
- }
- }
- }
-
- area->full = oldscreen;
- newa->full = oldscreen;
-
- ED_screen_change(C, screen);
+ screen = screen_state_to_nonnormal(C, win, toggle_area, state);
}
/* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */
@@ -1412,9 +1449,6 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
area->flag |= AREA_FLAG_STACKED_FULLSCREEN;
((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
}
- else if (ctx_area != NULL && ctx_area->spacetype == space_type) {
- area = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_area, SCREENMAXIMIZED);
- }
else {
area = ED_screen_full_newspace(C, ctx_area, (int)space_type);
((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 72b3b344813..51687d5de1d 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -3918,7 +3918,7 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Quad View";
- ot->description = "Split selected area into camera, front, right & top views";
+ ot->description = "Split selected area into camera, front, right, and top views";
ot->idname = "SCREEN_OT_region_quadview";
/* api callbacks */
@@ -5510,6 +5510,8 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(ED_OT_undo_history);
WM_operatortype_append(ED_OT_flush_edits);
+ WM_operatortype_append(ED_OT_lib_id_load_custom_preview);
+ WM_operatortype_append(ED_OT_lib_id_generate_preview);
}
/** \} */
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 9bf3d2610d8..fff8d27ef5b 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -32,6 +32,7 @@ set(INC
../../windowmanager
../../../../intern/atomic
../../../../intern/clog
+ ../../../../intern/eigen
../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 3a6b91443a0..324fd5d3075 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -40,6 +40,7 @@
#include "IMB_imbuf_types.h"
#include "DNA_brush_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@@ -934,7 +935,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Delta",
- "Delta offset of clone image in 0.0..1.0 coordinates",
+ "Delta offset of clone image in 0.0 to 1.0 coordinates",
-1.0f,
1.0f);
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 98f4b4013cb..cca4ffd4d78 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -6781,7 +6781,7 @@ static bool add_simple_uvs_poll(bContext *C)
void PAINT_OT_add_simple_uvs(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Add simple UVs";
+ ot->name = "Add Simple UVs";
ot->description = "Add cube map uvs on mesh";
ot->idname = "PAINT_OT_add_simple_uvs";
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 175d98ba9aa..3ca0d853d6a 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -43,7 +43,6 @@ struct wmEvent;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
-struct wmWindowManager;
enum ePaintMode;
enum ePaintSymmetryFlags;
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 17690757fa5..92c78a674f0 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1552,6 +1552,11 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (ss->totvert == 0) {
+ /* No geometry to trim or to detect a valid position for the trimming shape. */
+ return OPERATOR_CANCELLED;
+ }
+
SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op);
if (!sgcontext) {
return OPERATOR_CANCELLED;
@@ -1589,6 +1594,11 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (ss->totvert == 0) {
+ /* No geometry to trim or to detect a valid position for the trimming shape. */
+ return OPERATOR_CANCELLED;
+ }
+
SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op);
if (!sgcontext) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index fd7ec1da497..38d2bed7d97 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1227,6 +1227,7 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_LAYER,
SCULPT_TOOL_POSE,
+ SCULPT_TOOL_DISPLACEMENT_SMEAR,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_CLOTH,
SCULPT_TOOL_PAINT,
@@ -2362,6 +2363,7 @@ static float brush_strength(const Sculpt *sd,
final_pressure = pressure * pressure;
return final_pressure * overlap * feather;
case SCULPT_TOOL_SMEAR:
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_CLAY_STRIPS:
/* Clay Strips needs less strength to compensate the curve. */
@@ -3103,6 +3105,147 @@ static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **node
/** \} */
+/** \name Sculpt Multires Displacement Smear Brush
+ * \{ */
+
+static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float current_disp[3];
+ float current_disp_norm[3];
+ float interp_limit_surface_disp[3];
+
+ copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]);
+
+ switch (brush->smear_deform_type) {
+ case BRUSH_SMEAR_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SMEAR_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SMEAR_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ float weights_accum = 1.0f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ float neighbor_limit_co[3];
+ SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ sub_v3_v3v3(vertex_disp,
+ ss->cache->limit_surface_co[ni.index],
+ ss->cache->limit_surface_co[vd.index]);
+ const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index];
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
+ const float disp_interp = clamp_f(
+ -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+ madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp);
+ weights_accum += disp_interp;
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum);
+
+ float new_co[3];
+ add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp);
+ interp_v3_v3v3(vd.co, vd.co, new_co, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_displacement_smear_store_prev_disp_task_cb_ex(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
+ SCULPT_vertex_co_get(ss, vd.index),
+ ss->cache->limit_surface_co[vd.index]);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_curvemapping_init(brush->curve);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ if (!ss->cache->prev_displacement) {
+ ss->cache->prev_displacement = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "prev displacement");
+ ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
+ for (int i = 0; i < totvert; i++) {
+ SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ sub_v3_v3v3(ss->cache->prev_displacement[i],
+ SCULPT_vertex_co_get(ss, i),
+ ss->cache->limit_surface_co[i]);
+ }
+ }
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
static void do_draw_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -5742,32 +5885,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
}
else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
- if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
- SculptSearchSphereData data = {
- .ss = ss,
- .sd = sd,
- .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
- .original = false,
- .ignore_fully_ineffective = false,
- .center = ss->cache->initial_location,
- };
- BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
- }
- if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC) {
- SculptSearchSphereData data = {
- .ss = ss,
- .sd = sd,
- .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
- .original = false,
- .ignore_fully_ineffective = false,
- .center = ss->cache->location,
- };
- BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
- }
- else {
- /* Gobal simulation, get all nodes. */
- BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
- }
+ nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
@@ -5805,6 +5923,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
}
+ /* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with
+ * zero radius, thus we have no pbvh nodes on the first brush step. */
+ if (totnode ||
+ ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
+ ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
+ }
+ }
+ }
+
/* Only act if some verts are inside the brush area. */
if (totnode) {
float location[3];
@@ -5828,12 +5957,6 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
update_brush_local_mat(sd, ob);
}
- if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
- if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
- ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
- }
- }
-
if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) {
SCULPT_pose_brush_init(sd, ob, ss, brush);
}
@@ -5952,6 +6075,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_DISPLACEMENT_ERASER:
do_displacement_eraser_brush(sd, ob, nodes, totnode);
break;
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
+ do_displacement_smear_brush(sd, ob, nodes, totnode);
+ break;
case SCULPT_TOOL_PAINT:
SCULPT_do_paint_brush(sd, ob, nodes, totnode);
break;
@@ -6515,6 +6641,8 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Draw Face Sets";
case SCULPT_TOOL_DISPLACEMENT_ERASER:
return "Multires Displacement Eraser";
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
+ return "Multires Displacement Smear";
case SCULPT_TOOL_PAINT:
return "Paint Brush";
case SCULPT_TOOL_SMEAR:
@@ -6535,6 +6663,8 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->layer_displacement_factor);
MEM_SAFE_FREE(cache->prev_colors);
MEM_SAFE_FREE(cache->detail_directions);
+ MEM_SAFE_FREE(cache->prev_displacement);
+ MEM_SAFE_FREE(cache->limit_surface_co);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -6706,6 +6836,7 @@ static void sculpt_update_cache_invariants(
SCULPT_TOOL_MASK,
SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_SIMPLIFY,
+ SCULPT_TOOL_DISPLACEMENT_SMEAR,
SCULPT_TOOL_DISPLACEMENT_ERASER) &&
(sd->gravity_factor > 0.0f));
/* Get gravity vector in world space. */
@@ -8685,7 +8816,7 @@ static int sculpt_sample_color_invoke(bContext *C,
static void SCULPT_OT_sample_color(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Sample color";
+ ot->name = "Sample Color";
ot->idname = "SCULPT_OT_sample_color";
ot->description = "Sample the vertex color of the active vertex";
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index f8165890cc4..0ac0d796ca4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -119,6 +119,43 @@ static void cloth_brush_simulation_location_get(SculptSession *ss,
copy_v3_v3(r_location, ss->cache->location);
}
+PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss,
+ Brush *brush,
+ int *r_totnode)
+{
+ BLI_assert(ss->cache);
+ BLI_assert(brush->sculpt_tool == SCULPT_TOOL_CLOTH);
+ PBVHNode **nodes = NULL;
+
+ switch (brush->cloth_simulation_area_type) {
+ case BRUSH_CLOTH_SIMULATION_AREA_LOCAL: {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
+ .original = false,
+ .ignore_fully_ineffective = false,
+ .center = ss->cache->initial_location,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
+ } break;
+ case BRUSH_CLOTH_SIMULATION_AREA_GLOBAL:
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, r_totnode);
+ break;
+ case BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC: {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
+ .original = false,
+ .ignore_fully_ineffective = false,
+ .center = ss->cache->location,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
+ } break;
+ }
+
+ return nodes;
+}
+
static float cloth_brush_simulation_falloff_get(const Brush *brush,
const float radius,
const float location[3],
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index ddf5b39f080..aa1d407dc24 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -363,7 +363,7 @@ void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
0,
SHRT_MAX,
"Location",
- "Screen Coordinates of sampling",
+ "Screen coordinates of sampling",
0,
SHRT_MAX);
RNA_def_enum(ot->srna,
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index ad42750bb92..1fba958d695 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -41,6 +41,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
#include "BKE_mesh_mapping.h"
#include "BKE_multires.h"
#include "BKE_node.h"
@@ -1024,6 +1025,8 @@ typedef enum eSculptFaceSetEditMode {
SCULPT_FACE_SET_EDIT_GROW = 0,
SCULPT_FACE_SET_EDIT_SHRINK = 1,
SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2,
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3,
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4,
} eSculptFaceSetEditMode;
static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
@@ -1048,6 +1051,22 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
"Delete Geometry",
"Deletes the faces that are assigned to the Face Set",
},
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS,
+ "FAIR_POSITIONS",
+ 0,
+ "Fair Positions",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex positions",
+ },
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY,
+ "FAIR_TANGENCY",
+ 0,
+ "Fair Tangency",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex tangents",
+ },
{0, NULL, 0, NULL, NULL},
};
@@ -1181,6 +1200,29 @@ static void sculpt_face_set_delete_geometry(Object *ob,
BM_mesh_free(bm);
}
+static void sculpt_face_set_edit_fair_face_set(Object *ob,
+ const int active_face_set_id,
+ const int fair_order)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ Mesh *mesh = ob->data;
+ bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices");
+
+ SCULPT_boundary_info_ensure(ob);
+
+ for (int i = 0; i < totvert; i++) {
+ fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
+ SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
+ SCULPT_vertex_has_unique_face_set(ss, i);
+ }
+
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+ BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
+ MEM_freeN(fair_vertices);
+}
+
static void sculpt_face_set_apply_edit(Object *ob,
const int active_face_set_id,
const int mode,
@@ -1204,6 +1246,12 @@ static void sculpt_face_set_apply_edit(Object *ob,
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION);
+ break;
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY);
+ break;
}
}
@@ -1230,6 +1278,16 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
return false;
}
}
+
+ if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ /* TODO: Multires topology representation using grids and duplicates can't be used directly
+ * by the fair algorithm. Multires topology needs to be exposed in a different way or
+ * converted to a mesh for this operation. */
+ return false;
+ }
+ }
+
return true;
}
@@ -1287,11 +1345,38 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob,
MEM_freeN(nodes);
}
+static void sculpt_face_set_edit_modify_coordinates(bContext *C,
+ Object *ob,
+ const int active_face_set,
+ const eSculptFaceSetEditMode mode)
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ PBVH *pbvh = ss->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ SCULPT_undo_push_begin(ob, "face set edit");
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update(nodes[i]);
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
+ }
+ sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false);
+
+ if (ss->deform_modifiers_active || ss->shapekey_active) {
+ SCULPT_flush_stroke_deform(sd, ob, true);
+ }
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
+ SCULPT_undo_push_end();
+ MEM_freeN(nodes);
+}
+
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const int mode = RNA_enum_get(op->ptr, "mode");
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
@@ -1320,6 +1405,10 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
case SCULPT_FACE_SET_EDIT_SHRINK:
sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode);
+ break;
}
SCULPT_tag_update_overlays(C);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index f3c07a86201..76a6b05cdff 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -328,9 +328,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* rna */
- RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
+ RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", "");
RNA_def_float(
- ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
PropertyRNA *prop = RNA_def_float_color(
ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index ddad6bef7fd..0297ed73dd4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -80,12 +80,12 @@ static EnumPropertyItem prop_mask_filter_types[] = {
{MASK_FILTER_CONTRAST_INCREASE,
"CONTRAST_INCREASE",
0,
- "Increase contrast",
+ "Increase Contrast",
"Increase the contrast of the paint mask"},
{MASK_FILTER_CONTRAST_DECREASE,
"CONTRAST_DECREASE",
0,
- "Decrease contrast",
+ "Decrease Contrast",
"Decrease the contrast of the paint mask"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 02d4be20e1b..e11894a8c01 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -771,15 +771,15 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
"type",
prop_mesh_filter_types,
MESH_FILTER_INFLATE,
- "Filter type",
+ "Filter Type",
"Operation that is going to be applied to the mesh");
RNA_def_float(
- ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
RNA_def_enum_flag(ot->srna,
"deform_axis",
prop_mesh_filter_deform_axis_items,
MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z,
- "Deform axis",
+ "Deform Axis",
"Apply the deformation in the selected axis");
RNA_def_enum(ot->srna,
"orientation",
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 3b48207f461..d1e17c7e59b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -39,7 +39,6 @@
struct AutomaskingCache;
struct KeyBlock;
struct Object;
-struct SculptPoseIKChainSegment;
struct SculptUndoNode;
struct bContext;
@@ -423,6 +422,10 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
const float outline_col[3],
float outline_alpha);
+PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss,
+ Brush *brush,
+ int *r_totnode);
+
BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
{
return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type,
@@ -452,7 +455,7 @@ BLI_INLINE bool SCULPT_tool_needs_all_pbvh_nodes(const Brush *brush)
if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
/* Boundary needs all nodes because it is not possible to know where the boundary
* deformation is going to be propagated before calculating it. */
- /* TODO: after calculating the boudnary info in the first iteration, it should be
+ /* TODO: after calculating the boundary info in the first iteration, it should be
* possible to get the nodes that have vertices included in any boundary deformation
* and cache them. */
return true;
@@ -923,6 +926,10 @@ typedef struct StrokeCache {
float (*prev_colors)[4];
+ /* Multires Displacement Smear. */
+ float (*prev_displacement)[3];
+ float (*limit_surface_co)[3];
+
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 167215b3813..d186cafb857 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -1827,7 +1827,7 @@ static const EnumPropertyItem prop_actkeys_mirror_types[] = {
{ACTKEYS_MIRROR_XAXIS,
"XAXIS",
0,
- "By Values Over Value=0",
+ "By Values Over Zero Value",
"Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
{ACTKEYS_MIRROR_MARKER,
"MARKER",
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index ab5f1e0ab22..98e39520e99 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -1325,8 +1325,8 @@ void ACTION_OT_select_less(wmOperatorType *ot)
/* defines for left-right select tool */
static const EnumPropertyItem prop_actkeys_leftright_select_types[] = {
{ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
- {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
- {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
+ {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 10ce7b81954..0fe94ec382c 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -40,6 +40,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_clip.h"
#include "ED_curve.h"
#include "ED_fileselect.h"
@@ -105,6 +106,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_screen();
ED_operatortypes_anim();
ED_operatortypes_animchannels();
+ ED_operatortypes_asset();
ED_operatortypes_gpencil();
ED_operatortypes_object();
ED_operatortypes_lattice();
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index fa3e6a51036..c71e5e49d8d 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -51,6 +51,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 8b39995a5c9..c1f29231f96 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -246,9 +246,11 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
return true;
}
#endif
+#ifdef WITH_POINT_CLOUD
if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
return true;
}
+#endif
if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
return true;
}
@@ -812,7 +814,9 @@ const char *buttons_context_dir[] = {
#ifdef WITH_HAIR_NODES
"hair",
#endif
+#ifdef WITH_POINT_CLOUD
"pointcloud",
+#endif
"volume",
NULL,
};
@@ -822,6 +826,11 @@ int /*eContextResult*/ buttons_context(const bContext *C,
bContextDataResult *result)
{
SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts && sbuts->path == NULL) {
+ /* path is cleared for SCREEN_OT_redo_last, when global undo does a file-read which clears the
+ * path (see lib_link_workspace_layout_restore). */
+ buttons_context_compute(C, sbuts);
+ }
ButsContextPath *path = sbuts ? sbuts->path : NULL;
if (!path) {
@@ -899,10 +908,12 @@ int /*eContextResult*/ buttons_context(const bContext *C,
return CTX_RESULT_OK;
}
#endif
+#ifdef WITH_POINT_CLOUD
if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
return CTX_RESULT_OK;
}
+#endif
if (CTX_data_equals(member, "volume")) {
set_pointer_type(path, result, &RNA_Volume);
return CTX_RESULT_OK;
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 0a0846cf216..74e7bc11c26 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -35,7 +35,6 @@ struct bContext;
struct bContextDataResult;
struct bNode;
struct bNodeTree;
-struct uiLayout;
struct wmOperatorType;
struct SpaceProperties_Runtime {
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index 8bdc2ed993f..8f57abf83ae 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -338,7 +338,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
is_relative = BLI_path_is_rel(str);
}
- if (UNLIKELY(ptr.data == &U)) {
+ if (UNLIKELY(ptr.data == &U || is_userdef)) {
is_relative = false;
}
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 3f00e3114a5..8f36ccb019a 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -859,7 +859,7 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Location",
- "Cursor location in normalized (0.0-1.0) coordinates",
+ "Cursor location in normalized (0.0 to 1.0) coordinates",
-10.0f,
10.0f);
RNA_def_property_flag(prop, PROP_HIDDEN);
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index a54faa41122..9b8e9e0e871 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -164,12 +164,12 @@ static bool id_drop_poll(bContext *UNUSED(C),
const wmEvent *UNUSED(event),
const char **UNUSED(tooltip))
{
- return WM_drag_ID(drag, 0) != NULL;
+ return WM_drag_get_local_ID(drag, 0) != NULL;
}
static void id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
char *text = RNA_path_full_ID_py(G_MAIN, id);
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index e3bdda7c480..f2f7f9d82f9 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -25,6 +25,7 @@
#include <math.h>
#include <string.h>
+#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
#include "BLI_math.h"
@@ -134,6 +135,7 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
}
static void file_draw_icon(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -157,8 +159,29 @@ static void file_draw_icon(uiBlock *block,
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
if (drag) {
- /* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_path(but, BLI_strdup(path), true);
+ /* TODO duplicated from file_draw_preview(). */
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ ImBuf *preview_image = filelist_file_getimage(file);
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(but,
+ file->name,
+ BLI_strdup(blend_path),
+ file->blentype,
+ icon,
+ preview_image,
+ UI_DPI_FAC);
+ }
+ }
+ else {
+ /* path is no more static, cannot give it directly to but... */
+ UI_but_drag_set_path(but, BLI_strdup(path), true);
+ }
}
}
@@ -200,6 +223,65 @@ static void file_draw_string(int sx,
});
}
+/**
+ * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on
+ * completion.
+ */
+static void file_draw_string_multiline(int sx,
+ int sy,
+ const char *string,
+ int wrap_width,
+ int line_height,
+ const uchar text_col[4],
+ int *r_sx,
+ int *r_sy)
+{
+ rcti rect;
+
+ if (string[0] == '\0' || wrap_width < 1) {
+ return;
+ }
+
+ const uiStyle *style = UI_style_get();
+ int font_id = style->widgetlabel.uifont_id;
+ int len = strlen(string);
+
+ rctf textbox;
+ BLF_wordwrap(font_id, wrap_width);
+ BLF_enable(font_id, BLF_WORD_WRAP);
+ BLF_boundbox(font_id, string, len, &textbox);
+ BLF_disable(font_id, BLF_WORD_WRAP);
+
+ /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
+ * (for buttons it works) */
+ rect.xmin = sx;
+ rect.xmax = sx + wrap_width;
+ /* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will
+ * actually start drawing at (ymax - line-height). */
+ rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height;
+ rect.ymax = sy;
+
+ struct ResultBLF result;
+ UI_fontstyle_draw_ex(&style->widget,
+ &rect,
+ string,
+ text_col,
+ &(struct uiFontStyleDraw_Params){
+ .align = UI_STYLE_TEXT_LEFT,
+ .word_wrap = true,
+ },
+ len,
+ NULL,
+ NULL,
+ &result);
+ if (r_sx) {
+ *r_sx = result.width;
+ }
+ if (r_sy) {
+ *r_sy = rect.ymin + line_height;
+ }
+}
+
void file_calc_previews(const bContext *C, ARegion *region)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -210,6 +292,7 @@ void file_calc_previews(const bContext *C, ARegion *region)
}
static void file_draw_preview(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -218,7 +301,6 @@ static void file_draw_preview(uiBlock *block,
const int icon,
FileLayout *layout,
const bool is_icon,
- const int typeflags,
const bool drag,
const bool dimmed,
const bool is_link)
@@ -232,7 +314,7 @@ static void file_draw_preview(uiBlock *block,
float scale;
int ex, ey;
bool show_outline = !is_icon &&
- (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ (file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
BLI_assert(imb != NULL);
@@ -273,14 +355,14 @@ static void file_draw_preview(uiBlock *block,
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (is_icon) {
- if (typeflags & FILE_TYPE_DIR) {
+ if (file->typeflag & FILE_TYPE_DIR) {
UI_GetThemeColor4fv(TH_ICON_FOLDER, col);
}
else {
UI_GetThemeColor4fv(TH_TEXT, col);
}
}
- else if (typeflags & FILE_TYPE_FTFONT) {
+ else if (file->typeflag & FILE_TYPE_FTFONT) {
UI_GetThemeColor4fv(TH_TEXT, col);
}
@@ -288,7 +370,7 @@ static void file_draw_preview(uiBlock *block,
col[3] *= 0.3f;
}
- if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) {
+ if (!is_icon && file->typeflag & FILE_TYPE_BLENDERLIB) {
/* Datablock preview images use premultiplied alpha. */
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
@@ -324,7 +406,7 @@ static void file_draw_preview(uiBlock *block,
icon_color[2] = 255;
}
icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
- icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.75f));
+ icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f));
UI_icon_draw_ex(
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
@@ -346,13 +428,13 @@ static void file_draw_preview(uiBlock *block,
/* Link to folder or non-previewed file. */
uchar icon_color[4];
UI_GetThemeColor4ubv(TH_BACK, icon_color);
- icon_x = xco + ((typeflags & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
- icon_y = yco + ((typeflags & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
+ icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
+ icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
UI_icon_draw_ex(
icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false);
}
}
- else if (icon && !is_icon && !(typeflags & FILE_TYPE_FTFONT)) {
+ else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) {
/* Smaller, fainter icon at bottom-left for preview image thumbnail, but not for fonts. */
float icon_x, icon_y;
const uchar dark[4] = {0, 0, 0, 255};
@@ -385,8 +467,22 @@ static void file_draw_preview(uiBlock *block,
/* dragregion */
if (drag) {
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
/* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(
+ but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale);
+ }
+ }
+ else {
+ UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ }
}
GPU_blend(GPU_BLEND_NONE);
@@ -821,6 +917,7 @@ void file_draw_list(const bContext *C, ARegion *region)
}
file_draw_preview(block,
+ file,
path,
sx,
sy,
@@ -829,13 +926,13 @@ void file_draw_list(const bContext *C, ARegion *region)
icon,
layout,
is_icon,
- file->typeflag,
do_drag,
is_hidden,
is_link);
}
else {
file_draw_icon(block,
+ file,
path,
sx,
sy - layout->tile_border_y,
@@ -906,3 +1003,66 @@ void file_draw_list(const bContext *C, ARegion *region)
layout->curr_size = params->thumbnail_size;
}
+
+static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region)
+{
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+
+ char library_ui_path[PATH_MAX];
+ file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
+
+ uchar text_col[4];
+ uchar text_alert_col[4];
+ UI_GetThemeColor4ubv(TH_TEXT, text_col);
+ UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col);
+
+ const View2D *v2d = &region->v2d;
+ const int pad = sfile->layout->tile_border_x;
+ const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad);
+ const int line_height = sfile->layout->textheight;
+ int sx = v2d->tot.xmin + pad;
+ /* For some reason no padding needed. */
+ int sy = v2d->tot.ymax;
+
+ {
+ const char *message = TIP_("Library not found");
+ const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path);
+ char *draw_string = alloca(draw_string_str_len);
+ BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path);
+ file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy);
+ }
+
+ /* Next line, but separate it a bit further. */
+ sy -= line_height;
+
+ {
+ UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
+
+ const char *suggestion = TIP_(
+ "Set up the library or edit libraries in the Preferences, File Paths section.");
+ file_draw_string_multiline(
+ sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL);
+ }
+}
+
+/**
+ * Draw a string hint if the file list is invalid.
+ * \return true if the list is invalid and a hint was drawn.
+ */
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
+{
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ /* Only for asset browser. */
+ if (!ED_fileselect_is_asset_browser(sfile)) {
+ return false;
+ }
+ /* Check if the library exists. */
+ if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
+ return false;
+ }
+
+ file_draw_invalid_library_hint(sfile, region);
+
+ return true;
+}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index b459c02d9e5..56fb588776e 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -38,6 +38,7 @@ struct View2D;
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region);
void file_draw_check_ex(bContext *C, struct ScrArea *area);
void file_draw_check(bContext *C);
@@ -90,6 +91,7 @@ void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct Sp
void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op);
/* filesel.c */
+void fileselect_refresh_params(struct SpaceFile *sfile);
void fileselect_file_set(SpaceFile *sfile, const int index);
bool file_attribute_column_type_enabled(const FileSelectParams *params,
FileAttributeColumnType column);
@@ -116,3 +118,5 @@ void file_execute_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
+
+void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index b98348307f3..3730174a6c7 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -40,6 +40,7 @@
# include "BLI_winstuff.h"
#endif
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
@@ -930,6 +931,7 @@ void FILE_OT_select_all(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);
@@ -1836,10 +1838,6 @@ static int file_previous_exec(bContext *C, wmOperator *UNUSED(op))
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- if (!sfile->folders_next) {
- sfile->folders_next = folderlist_new();
- }
-
folderlist_pushdir(sfile->folders_next, params->dir);
folderlist_popdir(sfile->folders_prev, params->dir);
folderlist_pushdir(sfile->folders_next, params->dir);
@@ -1874,10 +1872,6 @@ static int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- if (!sfile->folders_next) {
- sfile->folders_next = folderlist_new();
- }
-
folderlist_pushdir(sfile->folders_prev, params->dir);
folderlist_popdir(sfile->folders_next, params->dir);
@@ -2702,6 +2696,29 @@ static bool file_delete_poll(bContext *C)
return poll;
}
+static bool file_delete_single(const FileSelectParams *params,
+ FileDirEntry *file,
+ const char **r_error_message)
+{
+ if (file->typeflag & FILE_TYPE_ASSET) {
+ ID *id = filelist_file_get_id(file);
+ if (!id) {
+ *r_error_message = "File is not a local data-block asset.";
+ return false;
+ }
+ ED_asset_clear_id(id);
+ }
+ else {
+ char str[FILE_MAX];
+ BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
+ if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int file_delete_exec(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -2715,9 +2732,7 @@ static int file_delete_exec(bContext *C, wmOperator *op)
for (int i = 0; i < numfiles; i++) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
FileDirEntry *file = filelist_file(sfile->files, i);
- char str[FILE_MAX];
- BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
- if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) {
+ if (!file_delete_single(params, file, &error_message)) {
report_error = true;
}
}
@@ -2763,13 +2778,20 @@ void FILE_OT_delete(struct wmOperatorType *ot)
static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op))
{
ScrArea *area = CTX_wm_area(C);
- ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_UI);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
ARegion *region_ctx = CTX_wm_region(C);
- CTX_wm_region_set(C, region);
- UI_textbutton_activate_rna(C, region, params, "filter_search");
+
+ if (area) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ CTX_wm_region_set(C, region);
+ if (UI_textbutton_activate_rna(C, region, params, "filter_search")) {
+ break;
+ }
+ }
+ }
+
CTX_wm_region_set(C, region_ctx);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c
index 452f2f704cf..9d85996c559 100644
--- a/source/blender/editors/space_file/file_utils.c
+++ b/source/blender/editors/space_file/file_utils.c
@@ -18,12 +18,14 @@
* \ingroup spfile
*/
+#include "BLI_fileops.h"
#include "BLI_listbase.h"
+#include "BLI_path_util.h"
#include "BLI_rect.h"
-
-#include "BLO_readfile.h"
+#include "BLI_string.h"
#include "BKE_context.h"
+#include "BLO_readfile.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
@@ -44,3 +46,14 @@ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int fil
ymax - layout->tile_h - layout->tile_border_y,
ymax);
}
+
+/**
+ * If \a path leads to a .blend, remove the trailing slash (if needed).
+ */
+void file_path_to_ui_path(const char *path, char *r_path, int max_size)
+{
+ char tmp_path[PATH_MAX];
+ BLI_strncpy(tmp_path, path, sizeof(tmp_path));
+ BLI_path_slash_rstrip(tmp_path);
+ BLI_strncpy(r_path, BLO_has_bfile_extension(tmp_path) ? tmp_path : path, max_size);
+}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index e87142a7096..d66219c7549 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -53,13 +53,18 @@
# include "BLI_winstuff.h"
#endif
+#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_idmap.h"
+#include "BKE_preferences.h"
#include "BLO_readfile.h"
+#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "ED_datafiles.h"
@@ -82,6 +87,8 @@
#include "filelist.h"
+#define FILEDIR_NBR_ENTRIES_UNSET -1
+
/* ----------------- FOLDERLIST (previous/next) -------------- */
typedef struct FolderList {
@@ -89,12 +96,6 @@ typedef struct FolderList {
char *foldername;
} FolderList;
-ListBase *folderlist_new(void)
-{
- ListBase *p = MEM_callocN(sizeof(*p), __func__);
- return p;
-}
-
void folderlist_popdir(struct ListBase *folderlist, char *dir)
{
const char *prev_dir;
@@ -117,6 +118,10 @@ void folderlist_popdir(struct ListBase *folderlist, char *dir)
void folderlist_pushdir(ListBase *folderlist, const char *dir)
{
+ if (!dir[0]) {
+ return;
+ }
+
struct FolderList *folder, *previous_folder;
previous_folder = folderlist->last;
@@ -153,7 +158,7 @@ int folderlist_clear_next(struct SpaceFile *sfile)
struct FolderList *folder;
/* if there is no folder_next there is nothing we can clear */
- if (!sfile->folders_next) {
+ if (BLI_listbase_is_empty(sfile->folders_next)) {
return 0;
}
@@ -180,23 +185,79 @@ void folderlist_free(ListBase *folderlist)
}
}
-ListBase *folderlist_duplicate(ListBase *folderlist)
+static ListBase folderlist_duplicate(ListBase *folderlist)
{
+ ListBase folderlistn = {NULL};
- if (folderlist) {
- ListBase *folderlistn = MEM_callocN(sizeof(*folderlistn), __func__);
- FolderList *folder;
+ BLI_duplicatelist(&folderlistn, folderlist);
+
+ for (FolderList *folder = folderlistn.first; folder; folder = folder->next) {
+ folder->foldername = MEM_dupallocN(folder->foldername);
+ }
+ return folderlistn;
+}
- BLI_duplicatelist(folderlistn, folderlist);
+/* ----------------- Folder-History (wraps/owns file list above) -------------- */
- for (folder = folderlistn->first; folder; folder = folder->next) {
- folder->foldername = MEM_dupallocN(folder->foldername);
+static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode)
+{
+ LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) {
+ if (history->browse_mode == browse_mode) {
+ return history;
}
- return folderlistn;
}
+
return NULL;
}
+void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile)
+{
+ FileFolderHistory *history = folder_history_find(sfile, sfile->browse_mode);
+
+ if (!history) {
+ history = MEM_callocN(sizeof(*history), __func__);
+ history->browse_mode = sfile->browse_mode;
+ BLI_addtail(&sfile->folder_histories, history);
+ }
+
+ sfile->folders_next = &history->folders_next;
+ sfile->folders_prev = &history->folders_prev;
+}
+
+static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history)
+{
+ if (sfile->folders_prev == &history->folders_prev) {
+ sfile->folders_prev = NULL;
+ }
+ if (sfile->folders_next == &history->folders_next) {
+ sfile->folders_next = NULL;
+ }
+ folderlist_free(&history->folders_prev);
+ folderlist_free(&history->folders_next);
+ BLI_freelinkN(&sfile->folder_histories, history);
+}
+
+void folder_history_list_free(SpaceFile *sfile)
+{
+ LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) {
+ folder_history_entry_free(sfile, history);
+ }
+}
+
+ListBase folder_history_list_duplicate(ListBase *listbase)
+{
+ ListBase histories = {NULL};
+
+ LISTBASE_FOREACH (FileFolderHistory *, history, listbase) {
+ FileFolderHistory *history_new = MEM_dupallocN(history);
+ history_new->folders_prev = folderlist_duplicate(&history->folders_prev);
+ history_new->folders_next = folderlist_duplicate(&history->folders_next);
+ BLI_addtail(&histories, history_new);
+ }
+
+ return histories;
+}
+
/* ------------------FILELIST------------------------ */
typedef struct FileListInternEntry {
@@ -216,6 +277,24 @@ typedef struct FileListInternEntry {
/** not strictly needed, but used during sorting, avoids to have to recompute it there... */
char *name;
+ /**
+ * This is data from the current main, represented by this file. It's crucial that this is
+ * updated correctly on undo, redo and file reading (without UI). The space is responsible to
+ * take care of that.
+ */
+ struct {
+ /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), the ID this file entry represents. */
+ ID *id;
+
+ /* For the few file types that have the preview already in memory. For others, there's delayed
+ * preview reading from disk. Non-owning pointer. */
+ PreviewImage *preview_image;
+ } local_data;
+
+ /** When the file represents an asset read from another file, it is stored here.
+ * Owning pointer. */
+ AssetMetaData *imported_asset_data;
+
/** Defined in BLI_fileops.h */
eFileAttributes attributes;
BLI_stat_t st;
@@ -267,7 +346,11 @@ typedef struct FileListEntryPreview {
char path[FILE_MAX];
uint flags;
int index;
- ImBuf *img;
+ /* Some file types load the memory from runtime data, not from disk. We just wait until it's done
+ * generating (BKE_previewimg_is_finished()). */
+ PreviewImage *in_memory_preview;
+
+ int icon_id;
} FileListEntryPreview;
/* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing
@@ -290,11 +373,16 @@ enum {
FLF_HIDE_DOT = 1 << 1,
FLF_HIDE_PARENT = 1 << 2,
FLF_HIDE_LIB_DIR = 1 << 3,
+ FLF_ASSETS_ONLY = 1 << 4,
};
typedef struct FileList {
FileDirEntryArr filelist;
+ eFileSelectType type;
+ /* The library this list was created for. Stored here so we know when to re-read. */
+ FileSelectAssetLibraryUID *asset_library;
+
short flags;
short sort;
@@ -324,10 +412,13 @@ typedef struct FileList {
bool (*checkdirf)(struct FileList *, char *, const bool);
/* Fill filelist (to be called by read job). */
- void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+ void (*read_jobf)(
+ Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
/* Filter an entry of current filelist. */
bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *);
+
+ short tags; /* FileListTags */
} FileList;
/* FileList.flags */
@@ -340,6 +431,14 @@ enum {
FL_SORT_INVERT = 1 << 5,
};
+/* FileList.tags */
+enum FileListTags {
+ /** The file list has references to main data (IDs) and needs special care. */
+ FILELIST_TAGS_USES_MAIN_DATA = (1 << 0),
+ /** The file list type is not thread-safe. */
+ FILELIST_TAGS_NO_THREADS = (1 << 2),
+};
+
#define SPECIAL_IMG_SIZE 256
#define SPECIAL_IMG_ROWS 1
#define SPECIAL_IMG_COLS 7
@@ -357,24 +456,34 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
@@ -694,6 +803,11 @@ static bool is_filtered_hidden(const char *filename,
return true;
}
#endif
+ /* For data-blocks (but not the group directories), check the asset-only filter. */
+ if (!(file->typeflag & FILE_TYPE_DIR) && (file->typeflag & FILE_TYPE_BLENDERLIB) &&
+ (filter->flags & FLF_ASSETS_ONLY) && !(file->typeflag & FILE_TYPE_ASSET)) {
+ return true;
+ }
return false;
}
@@ -737,51 +851,61 @@ static bool is_filtered_file(FileListInternEntry *file,
return is_filtered;
}
-static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_id_file(const FileListInternEntry *file,
+ const char *id_group,
+ const char *name,
+ const FileListFilter *filter)
{
- bool is_filtered;
- char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
-
- BLI_join_dirfile(path, sizeof(path), root, file->relpath);
-
- if (BLO_library_path_explode(path, dir, &group, &name)) {
- is_filtered = !is_filtered_hidden(file->relpath, filter, file);
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
- if (file->typeflag & FILE_TYPE_DIR) {
- if (file->typeflag &
- (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
- if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
- is_filtered = false;
- }
- }
- else {
- if (!(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
- }
+ bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
+ if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
+ /* We only check for types if some type are enabled in filtering. */
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
+ if (file->typeflag & FILE_TYPE_DIR) {
+ if (file->typeflag &
+ (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ is_filtered = false;
}
}
- if (is_filtered && group) {
- if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ else {
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
is_filtered = false;
}
- else {
- uint64_t filter_id = groupname_to_filter_id(group);
- if (!(filter_id & filter->filter_id)) {
- is_filtered = false;
- }
- }
}
}
- /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
- if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ if (is_filtered && id_group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
is_filtered = false;
}
+ else {
+ uint64_t filter_id = groupname_to_filter_id(id_group);
+ if (!(filter_id & filter->filter_id)) {
+ is_filtered = false;
+ }
+ }
+ }
+ }
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ if (is_filtered && (filter->filter_search[0] != '\0')) {
+ if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ is_filtered = false;
}
}
}
+
+ return is_filtered;
+}
+
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+{
+ bool is_filtered;
+ char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
+
+ BLI_join_dirfile(path, sizeof(path), root, file->relpath);
+
+ if (BLO_library_path_explode(path, dir, &group, &name)) {
+ is_filtered = is_filtered_id_file(file, group, name, filter);
+ }
else {
is_filtered = is_filtered_file(file, root, filter);
}
@@ -796,6 +920,14 @@ static bool is_filtered_main(FileListInternEntry *file,
return !is_filtered_hidden(file->relpath, filter, file);
}
+static bool is_filtered_main_assets(FileListInternEntry *file,
+ const char *UNUSED(dir),
+ FileListFilter *filter)
+{
+ /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */
+ return is_filtered_id_file(file, file->relpath, file->name, filter);
+}
+
static void filelist_filter_clear(FileList *filelist)
{
filelist->flags |= FL_NEED_FILTERING;
@@ -807,7 +939,7 @@ void filelist_filter(FileList *filelist)
const int num_files = filelist->filelist.nbr_entries;
FileListInternEntry **filtered_tmp, *file;
- if (filelist->filelist.nbr_entries == 0) {
+ if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
return;
}
@@ -858,6 +990,7 @@ void filelist_setfilter_options(FileList *filelist,
const bool hide_parent,
const uint64_t filter,
const uint64_t filter_id,
+ const bool filter_assets_only,
const char *filter_glob,
const char *filter_search)
{
@@ -875,6 +1008,10 @@ void filelist_setfilter_options(FileList *filelist,
filelist->filter_data.flags ^= FLF_HIDE_PARENT;
update = true;
}
+ if (((filelist->filter_data.flags & FLF_ASSETS_ONLY) != 0) != (filter_assets_only != 0)) {
+ filelist->filter_data.flags ^= FLF_ASSETS_ONLY;
+ update = true;
+ }
if (filelist->filter_data.filter != filter) {
filelist->filter_data.filter = filter;
update = true;
@@ -903,6 +1040,54 @@ 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)
+{
+ if (library_a->type != library_b->type) {
+ return false;
+ }
+ if (library_a->type == FILE_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);
+ return (library_ptr_a != NULL) &&
+ (library_a->custom_library_index == library_b->custom_library_index);
+ }
+
+ return true;
+}
+
+/**
+ * \param asset_library: May be NULL to unset the library.
+ */
+void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library)
+{
+ /* Unset if needed. */
+ if (!asset_library) {
+ if (filelist->asset_library) {
+ MEM_SAFE_FREE(filelist->asset_library);
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ return;
+ }
+
+ if (!filelist->asset_library) {
+ filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library),
+ "filelist asset library");
+ *filelist->asset_library = *asset_library;
+
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) {
+ *filelist->asset_library = *asset_library;
+ filelist->flags |= FL_FORCE_RESET;
+ }
+}
+
/* ********** Icon/image helpers ********** */
void filelist_init_icons(void)
@@ -960,7 +1145,12 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index)
{
FileDirEntry *file = filelist_geticon_get_file(filelist, index);
- return file->image;
+ return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
+}
+
+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)
@@ -988,12 +1178,12 @@ ImBuf *filelist_geticon_image(struct FileList *filelist, const int index)
return filelist_geticon_image_ex(file);
}
-static int filelist_geticon_ex(FileDirEntry *file,
+static int filelist_geticon_ex(const FileDirEntry *file,
const char *root,
const bool is_main,
const bool ignore_libdir)
{
- const int typeflag = file->typeflag;
+ const eFileSel_File_Types typeflag = file->typeflag;
if ((typeflag & FILE_TYPE_DIR) &&
!(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
@@ -1027,7 +1217,7 @@ static int filelist_geticon_ex(FileDirEntry *file,
if (file->redirection_path) {
target = file->redirection_path;
}
- else {
+ else if (root) {
BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
BLI_path_slash_ensure(fullpath);
}
@@ -1111,6 +1301,12 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m
return filelist_geticon_ex(file, filelist->filelist.root, is_main, false);
}
+int ED_file_icon(const FileDirEntry *file)
+{
+ return file->preview_icon_id ? file->preview_icon_id :
+ filelist_geticon_ex(file, NULL, false, false);
+}
+
/* ********** Main ********** */
static void parent_dir_until_exists_or_default_root(char *dir)
@@ -1160,6 +1356,14 @@ static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const
return filelist_checkdir_lib(filelist, r_dir, do_change);
}
+static bool filelist_checkdir_main_assets(struct FileList *UNUSED(filelist),
+ char *UNUSED(r_dir),
+ const bool UNUSED(do_change))
+{
+ /* Main is always valid. */
+ return true;
+}
+
static void filelist_entry_clear(FileDirEntry *entry)
{
if (entry->name) {
@@ -1174,8 +1378,9 @@ static void filelist_entry_clear(FileDirEntry *entry)
if (entry->redirection_path) {
MEM_freeN(entry->redirection_path);
}
- if (entry->image) {
- IMB_freeImBuf(entry->image);
+ if (entry->preview_icon_id) {
+ BKE_icon_delete(entry->preview_icon_id);
+ entry->preview_icon_id = 0;
}
/* For now, consider FileDirEntryRevision::poin as not owned here,
* so no need to do anything about it */
@@ -1232,8 +1437,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array)
#else
BLI_assert(BLI_listbase_is_empty(&array->entries));
#endif
- array->nbr_entries = 0;
- array->nbr_entries_filtered = -1;
+ array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
array->entry_idx_start = -1;
array->entry_idx_end = -1;
}
@@ -1249,6 +1454,10 @@ static void filelist_intern_entry_free(FileListInternEntry *entry)
if (entry->name) {
MEM_freeN(entry->name);
}
+ /* If we own the asset-data (it was generated from external file data), free it. */
+ if (entry->imported_asset_data) {
+ BKE_asset_metadata_free(&entry->imported_asset_data);
+ }
MEM_freeN(entry);
}
@@ -1272,37 +1481,54 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
FileListEntryPreview *preview = preview_taskdata->preview;
ThumbSource source = 0;
+ bool done = false;
// printf("%s: Start (%d)...\n", __func__, threadid);
- // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
- BLI_assert(preview->flags &
- (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
- FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
-
- if (preview->flags & FILE_TYPE_IMAGE) {
- source = THB_SOURCE_IMAGE;
- }
- else if (preview->flags &
- (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
- source = THB_SOURCE_BLEND;
- }
- else if (preview->flags & FILE_TYPE_MOVIE) {
- source = THB_SOURCE_MOVIE;
- }
- else if (preview->flags & FILE_TYPE_FTFONT) {
- source = THB_SOURCE_FONT;
+ if (preview->in_memory_preview) {
+ if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) {
+ ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW);
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ done = true;
+ }
}
+ else {
+ // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ BLI_assert(preview->flags &
+ (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
+ FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
- IMB_thumb_path_lock(preview->path);
- /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate in
- * case user switch to a bigger preview size. */
- preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
- IMB_thumb_path_unlock(preview->path);
+ if (preview->flags & FILE_TYPE_IMAGE) {
+ source = THB_SOURCE_IMAGE;
+ }
+ else if (preview->flags &
+ (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
+ source = THB_SOURCE_BLEND;
+ }
+ else if (preview->flags & FILE_TYPE_MOVIE) {
+ source = THB_SOURCE_MOVIE;
+ }
+ else if (preview->flags & FILE_TYPE_FTFONT) {
+ source = THB_SOURCE_FONT;
+ }
+
+ IMB_thumb_path_lock(preview->path);
+ /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
+ * in case user switch to a bigger preview size. */
+ ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source);
+ IMB_thumb_path_unlock(preview->path);
+ if (imbuf) {
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ }
+
+ done = true;
+ }
- /* 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);
+ if (done) {
+ /* 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);
+ }
// printf("%s: End (%d)...\n", __func__, threadid);
}
@@ -1315,8 +1541,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void
/* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent
* to previews_done queue. */
if (preview != NULL) {
- if (preview->img) {
- IMB_freeImBuf(preview->img);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1342,8 +1568,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
// preview->img);
- if (preview->img) {
- IMB_freeImBuf(preview->img);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1374,10 +1600,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- if (!entry->image && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
+ if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
if (entry->redirection_path) {
BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
@@ -1389,7 +1616,8 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
preview->index = index;
preview->flags = entry->typeflag;
- preview->img = NULL;
+ preview->in_memory_preview = intern_entry->local_data.preview_image;
+ preview->icon_id = 0;
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
filelist_cache_preview_ensure_running(cache);
@@ -1497,25 +1725,45 @@ FileList *filelist_new(short type)
p->selection_state = BLI_ghash_new(
BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
+ p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ filelist_settype(p, type);
- switch (type) {
+ return p;
+}
+
+void filelist_settype(FileList *filelist, short type)
+{
+ if (filelist->type == type) {
+ return;
+ }
+
+ filelist->type = type;
+ filelist->tags = 0;
+ switch (filelist->type) {
case FILE_MAIN:
- p->checkdirf = filelist_checkdir_main;
- p->read_jobf = filelist_readjob_main;
- p->filterf = is_filtered_main;
+ filelist->checkdirf = filelist_checkdir_main;
+ filelist->read_jobf = filelist_readjob_main;
+ filelist->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
- p->checkdirf = filelist_checkdir_lib;
- p->read_jobf = filelist_readjob_lib;
- p->filterf = is_filtered_lib;
+ filelist->checkdirf = filelist_checkdir_lib;
+ filelist->read_jobf = filelist_readjob_lib;
+ filelist->filterf = is_filtered_lib;
+ break;
+ case FILE_MAIN_ASSET:
+ filelist->checkdirf = filelist_checkdir_main_assets;
+ filelist->read_jobf = filelist_readjob_main_assets;
+ filelist->filterf = is_filtered_main_assets;
+ filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS;
break;
default:
- p->checkdirf = filelist_checkdir_dir;
- p->read_jobf = filelist_readjob_dir;
- p->filterf = is_filtered_file;
+ filelist->checkdirf = filelist_checkdir_dir;
+ filelist->read_jobf = filelist_readjob_dir;
+ filelist->filterf = is_filtered_file;
break;
}
- return p;
+
+ filelist->flags |= FL_FORCE_RESET;
}
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
@@ -1560,6 +1808,8 @@ void filelist_free(struct FileList *filelist)
filelist->selection_state = NULL;
}
+ MEM_SAFE_FREE(filelist->asset_library);
+
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
@@ -1580,7 +1830,7 @@ BlendHandle *filelist_lib(struct FileList *filelist)
static const char *fileentry_uiname(const char *root,
const char *relpath,
- const int typeflag,
+ const eFileSel_File_Types typeflag,
char *buff)
{
char *name = NULL;
@@ -1624,11 +1874,12 @@ bool filelist_is_dir(struct FileList *filelist, const char *path)
*/
void filelist_setdir(struct FileList *filelist, char *r_dir)
{
+ const bool allow_invalid = filelist->asset_library != NULL;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
- const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
- BLI_assert(is_valid_path);
+ const bool is_valid_path = filelist->checkdirf(filelist, r_dir, !allow_invalid);
+ BLI_assert(is_valid_path || allow_invalid);
UNUSED_VARS_NDEBUG(is_valid_path);
if (!STREQ(filelist->filelist.root, r_dir)) {
@@ -1645,11 +1896,16 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
}
}
-bool filelist_force_reset(struct FileList *filelist)
+bool filelist_needs_force_reset(FileList *filelist)
{
return (filelist->flags & FL_FORCE_RESET) != 0;
}
+void filelist_tag_force_reset(FileList *filelist)
+{
+ filelist->flags |= FL_FORCE_RESET;
+}
+
bool filelist_is_ready(struct FileList *filelist)
{
return (filelist->flags & FL_IS_READY) != 0;
@@ -1660,6 +1916,11 @@ bool filelist_pending(struct FileList *filelist)
return (filelist->flags & FL_IS_PENDING) != 0;
}
+bool filelist_needs_reset_on_main_changes(const FileList *filelist)
+{
+ return (filelist->tags & FILELIST_TAGS_USES_MAIN_DATA) != 0;
+}
+
/**
* Limited version of full update done by space_file's file_refresh(),
* to be used by operators and such.
@@ -1668,7 +1929,7 @@ bool filelist_pending(struct FileList *filelist)
*/
int filelist_files_ensure(FileList *filelist)
{
- if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) {
+ if (!filelist_needs_force_reset(filelist) || !filelist_needs_reading(filelist)) {
filelist_sort(filelist);
filelist_filter(filelist);
}
@@ -1701,6 +1962,17 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
if (entry->redirection_path) {
ret->redirection_path = BLI_strdup(entry->redirection_path);
}
+ ret->id = entry->local_data.id;
+ ret->asset_data = entry->imported_asset_data ? entry->imported_asset_data : NULL;
+ if (ret->id && (ret->asset_data == NULL)) {
+ ret->asset_data = ret->id->asset_data;
+ }
+ /* For some file types the preview is already available. */
+ if (entry->local_data.preview_image &&
+ BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) {
+ ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW);
+ ret->preview_icon_id = BKE_icon_imbuf_create(ibuf);
+ }
BLI_addtail(&cache->cached_entries, ret);
return ret;
}
@@ -1770,7 +2042,7 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
{
int fidx = -1;
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return fidx;
}
@@ -1788,9 +2060,17 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
return -1;
}
+/**
+ * Get the ID a file represents (if any). For #FILE_MAIN, #FILE_MAIN_ASSET.
+ */
+ID *filelist_file_get_id(const FileDirEntry *file)
+{
+ return file->id;
+}
+
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
{
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return NULL;
}
@@ -2147,15 +2427,17 @@ bool filelist_cache_previews_update(FileList *filelist)
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
- if (preview->img) {
+ if (preview->icon_id) {
/* Due to asynchronous process, a preview for a given image may be generated several times,
* i.e. entry->image may already be set at this point. */
- if (entry && !entry->image) {
- entry->image = preview->img;
+ if (entry && !entry->preview_icon_id) {
+ /* Move ownership over icon. */
+ entry->preview_icon_id = preview->icon_id;
+ preview->icon_id = 0;
changed = true;
}
else {
- IMB_freeImBuf(preview->img);
+ BKE_icon_delete(preview->icon_id);
}
}
else if (entry) {
@@ -2313,9 +2595,9 @@ int ED_file_extension_icon(const char *path)
}
}
-int filelist_empty(struct FileList *filelist)
+int filelist_needs_reading(struct FileList *filelist)
{
- return (filelist->filelist.nbr_entries == 0);
+ return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET);
}
uint filelist_entry_select_set(const FileList *filelist,
@@ -2564,8 +2846,8 @@ static int filelist_readjob_list_dir(const char *root,
static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
{
FileListInternEntry *entry;
- LinkNode *ln, *names;
- int i, nnames, idcode = 0, nbr_entries = 0;
+ LinkNode *ln, *names = NULL, *datablock_infos = NULL;
+ int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
bool ok;
@@ -2587,11 +2869,11 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
* and freed in filelist_entry_free. */
if (group) {
idcode = groupname_to_code(group);
- names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
+ datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
}
else {
names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nnames = BLI_linklist_count(names);
+ nitems = BLI_linklist_count(names);
}
BLO_blendhandle_close(libfiledata);
@@ -2604,12 +2886,18 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- for (i = 0, ln = names; i < nnames; i++, ln = ln->next) {
- const char *blockname = ln->link;
+ for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) {
+ struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL;
+ const char *blockname = info ? info->name : ln->link;
entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(blockname);
entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (info && info->asset_data) {
+ entry->typeflag |= FILE_TYPE_ASSET;
+ /* Moves ownership! */
+ entry->imported_asset_data = info->asset_data;
+ }
if (!(group && idcode)) {
entry->typeflag |= FILE_TYPE_DIR;
entry->blentype = groupname_to_code(blockname);
@@ -2621,7 +2909,7 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- BLI_linklist_freeN(names);
+ BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
return nbr_entries;
}
@@ -2820,7 +3108,10 @@ static void filelist_readjob_do(const bool do_lib,
// BLI_assert(filelist->filtered == NULL);
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == 0));
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
td_dir = BLI_stack_push_r(todo_dirs);
@@ -2932,7 +3223,8 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_free(todo_dirs);
}
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2942,7 +3234,8 @@ static void filelist_readjob_dir(FileList *filelist,
filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2952,7 +3245,8 @@ static void filelist_readjob_lib(FileList *filelist,
filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2960,12 +3254,67 @@ static void filelist_readjob_main(FileList *filelist,
ThreadMutex *lock)
{
/* TODO! */
- filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock);
+}
+
+/**
+ * \warning Acts on main, so NOT thread-safe!
+ */
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *UNUSED(main_name),
+ short *UNUSED(stop),
+ short *do_update,
+ float *UNUSED(progress),
+ ThreadMutex *UNUSED(lock))
+{
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
+
+ FileListInternEntry *entry;
+ ListBase tmp_entries = {0};
+ ID *id_iter;
+ int nbr_entries = 0;
+
+ FOREACH_MAIN_ID_BEGIN (current_main, id_iter) {
+ if (!id_iter->asset_data) {
+ continue;
+ }
+
+ const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name));
+
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(id_code_name);
+ entry->name = BLI_strdup(id_iter->name + 2);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET;
+ entry->blentype = GS(id_iter->name);
+ *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32(
+ (uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+ entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
+ id_iter);
+ entry->local_data.id = id_iter;
+ nbr_entries++;
+ BLI_addtail(&tmp_entries, entry);
+ }
+ FOREACH_MAIN_ID_END;
+
+ if (nbr_entries) {
+ *do_update = true;
+
+ BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries);
+ filelist->filelist.nbr_entries += nbr_entries;
+ filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start =
+ filelist->filelist.entry_idx_end = -1;
+ }
}
typedef struct FileListReadJob {
ThreadMutex lock;
char main_name[FILE_MAX];
+ Main *current_main;
struct FileList *filelist;
/** XXX We may use a simpler struct here... just a linked list and root path? */
struct FileList *tmp_filelist;
@@ -2985,7 +3334,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
- flrj->tmp_filelist->filelist.nbr_entries = 0;
+ flrj->tmp_filelist->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
@@ -2996,11 +3345,17 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist->libfiledata = NULL;
memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
flrj->tmp_filelist->selection_state = NULL;
+ flrj->tmp_filelist->asset_library = NULL;
BLI_mutex_unlock(&flrj->lock);
- flrj->tmp_filelist->read_jobf(
- flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
+ flrj->tmp_filelist->read_jobf(flrj->current_main,
+ flrj->tmp_filelist,
+ flrj->main_name,
+ stop,
+ do_update,
+ progress,
+ &flrj->lock);
}
static void filelist_readjob_update(void *flrjv)
@@ -3015,7 +3370,7 @@ static void filelist_readjob_update(void *flrjv)
BLI_mutex_lock(&flrj->lock);
- if (flrj->tmp_filelist->filelist.nbr_entries) {
+ if (flrj->tmp_filelist->filelist.nbr_entries > 0) {
/* We just move everything out of 'thread context' into final list. */
new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries;
BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
@@ -3033,7 +3388,7 @@ static void filelist_readjob_update(void *flrjv)
/* if no new_nbr_entries, this is NOP */
BLI_movelisttolist(&fl_intern->entries, &new_entries);
- flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries;
+ flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries;
}
static void filelist_readjob_endjob(void *flrjv)
@@ -3074,16 +3429,36 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
wmJob *wm_job;
FileListReadJob *flrj;
+ if (!filelist_is_dir(filelist, filelist->filelist.root)) {
+ return;
+ }
+
/* prepare job data */
flrj = MEM_callocN(sizeof(*flrj), __func__);
flrj->filelist = filelist;
+ flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
filelist->flags |= FL_IS_PENDING;
+ /* Init even for single threaded execution. Called functions use it. */
BLI_mutex_init(&flrj->lock);
+ if (filelist->tags & FILELIST_TAGS_NO_THREADS) {
+ short dummy_stop = false;
+ short dummy_do_update = false;
+ float dummy_progress = 0.0f;
+
+ /* Single threaded execution. Just directly call the callbacks. */
+ filelist_readjob_startjob(flrj, &dummy_stop, &dummy_do_update, &dummy_progress);
+ filelist_readjob_endjob(flrj);
+ filelist_readjob_free(flrj);
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+ return;
+ }
+
/* setup job */
wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 4b8cc052382..16984bb6e43 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -29,6 +29,7 @@ extern "C" {
struct BlendHandle;
struct FileList;
+struct FileSelectAssetLibraryUID;
struct FileSelection;
struct wmWindowManager;
@@ -46,14 +47,16 @@ typedef enum FileCheckType {
CHECK_ALL = 3,
} FileCheckType;
-struct ListBase *folderlist_new(void);
void folderlist_free(struct ListBase *folderlist);
-struct ListBase *folderlist_duplicate(ListBase *folderlist);
void folderlist_popdir(struct ListBase *folderlist, char *dir);
void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
const char *folderlist_peeklastdir(struct ListBase *folderlist);
int folderlist_clear_next(struct SpaceFile *sfile);
+void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile);
+void folder_history_list_free(struct SpaceFile *sfile);
+struct ListBase folder_history_list_duplicate(struct ListBase *listbase);
+
void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort);
void filelist_sort(struct FileList *filelist);
@@ -63,17 +66,22 @@ void filelist_setfilter_options(struct FileList *filelist,
const bool hide_parent,
const uint64_t filter,
const uint64_t filter_id,
+ const bool filter_assets_only,
const char *filter_glob,
const char *filter_search);
void filelist_filter(struct FileList *filelist);
+void filelist_setlibrary(struct FileList *filelist,
+ const struct FileSelectAssetLibraryUID *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(struct FileList *filelist, const int index);
int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
struct FileList *filelist_new(short type);
+void filelist_settype(struct FileList *filelist, short type);
void filelist_clear(struct FileList *filelist);
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
void filelist_free(struct FileList *filelist);
@@ -83,15 +91,18 @@ bool filelist_is_dir(struct FileList *filelist, const char *path);
void filelist_setdir(struct FileList *filelist, char *r_dir);
int filelist_files_ensure(struct FileList *filelist);
-int filelist_empty(struct FileList *filelist);
+int filelist_needs_reading(struct FileList *filelist);
FileDirEntry *filelist_file(struct FileList *filelist, int index);
int filelist_file_findpath(struct FileList *filelist, const char *file);
+struct ID *filelist_file_get_id(const struct FileDirEntry *file);
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);
void filelist_file_cache_slidingwindow_set(struct FileList *filelist, size_t window_size);
bool filelist_file_cache_block(struct FileList *filelist, const int index);
-bool filelist_force_reset(struct FileList *filelist);
+bool filelist_needs_force_reset(struct FileList *filelist);
+void filelist_tag_force_reset(struct FileList *filelist);
bool filelist_pending(struct FileList *filelist);
+bool filelist_needs_reset_on_main_changes(const struct FileList *filelist);
bool filelist_is_ready(struct FileList *filelist);
unsigned int filelist_entry_select_set(const struct FileList *filelist,
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 6e933e53a8f..b919a30e6cd 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -56,7 +56,9 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
+#include "BKE_idtype.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BLF_api.h"
@@ -77,14 +79,67 @@
#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X)
-FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
+static void fileselect_initialize_params_common(SpaceFile *sfile, FileSelectParams *params)
{
- if (!sfile) {
- /* Sometimes called in poll before space type was checked. */
- return NULL;
+ const char *blendfile_path = BKE_main_blendfile_path_from_global();
+
+ /* operator has no setting for this */
+ params->active_file = -1;
+
+ if (!params->dir[0]) {
+ if (blendfile_path[0] != '\0') {
+ BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir));
+ }
+ else {
+ const char *doc_path = BKE_appdir_folder_default();
+ if (doc_path) {
+ BLI_strncpy(params->dir, doc_path, sizeof(params->dir));
+ }
+ }
}
- return sfile->params;
+ folder_history_list_ensure_for_active_browse_mode(sfile);
+ folderlist_pushdir(sfile->folders_prev, params->dir);
+
+ /* Switching thumbnails needs to recalc layout T28809. */
+ if (sfile->layout) {
+ sfile->layout->dirty = true;
+ }
+}
+
+static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
+{
+ BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
+ BLI_assert(sfile->op == NULL);
+
+ FileAssetSelectParams *asset_params = sfile->asset_params;
+
+ if (!asset_params) {
+ 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.custom_library_index = -1;
+ }
+
+ FileSelectParams *base_params = &asset_params->base_params;
+ base_params->file[0] = '\0';
+ base_params->filter_glob[0] = '\0';
+ /* TODO this way of using filters to form categories is notably slower than specifying a
+ * "group" to read. That's because all types are read and filtering is applied afterwards. Would
+ * be nice if we could lazy-read individual groups. */
+ base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER;
+ base_params->flag &= ~FILE_DIRSEL_ONLY;
+ base_params->filter |= FILE_TYPE_BLENDERLIB;
+ base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR;
+ base_params->display = FILE_IMGDISPLAY;
+ base_params->sort = FILE_SORT_ALPHA;
+ base_params->recursion_level = 1;
+ /* 'SMALL' size by default. More reasonable since this is typically used as regular editor,
+ * space is more of an issue here. */
+ base_params->thumbnail_size = 96;
+
+ fileselect_initialize_params_common(sfile, base_params);
}
/**
@@ -92,6 +147,8 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
* the previously used settings to be used here rather than overriding them */
static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
{
+ BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES);
+
FileSelectParams *params;
wmOperator *op = sfile->op;
@@ -297,42 +354,104 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
params->filter_glob[0] = '\0';
}
- /* operator has no setting for this */
- params->active_file = -1;
+ fileselect_initialize_params_common(sfile, params);
- /* initialize the list with previous folders */
- if (!sfile->folders_prev) {
- sfile->folders_prev = folderlist_new();
- }
+ return params;
+}
- if (!params->dir[0]) {
- if (blendfile_path[0] != '\0') {
- BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir));
- }
- else {
- const char *doc_path = BKE_appdir_folder_default();
- if (doc_path) {
- BLI_strncpy(params->dir, doc_path, sizeof(params->dir));
+/**
+ * If needed, create and return the file select parameters for the active browse mode.
+ */
+FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile)
+{
+ switch ((eFileBrowse_Mode)sfile->browse_mode) {
+ case FILE_BROWSE_MODE_FILES:
+ if (!sfile->params) {
+ fileselect_ensure_updated_file_params(sfile);
}
- }
+ return sfile->params;
+ case FILE_BROWSE_MODE_ASSETS:
+ if (!sfile->asset_params) {
+ fileselect_ensure_updated_asset_params(sfile);
+ }
+ return &sfile->asset_params->base_params;
}
- folderlist_pushdir(sfile->folders_prev, params->dir);
+ BLI_assert(!"Invalid browse mode set in file space.");
+ return NULL;
+}
- /* Switching thumbnails needs to recalc layout T28809. */
- if (sfile->layout) {
- sfile->layout->dirty = true;
+/**
+ * Get the file select parameters for the active browse mode.
+ */
+FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
+{
+ if (!sfile) {
+ /* Sometimes called in poll before space type was checked. */
+ return NULL;
}
- return params;
+ switch ((eFileBrowse_Mode)sfile->browse_mode) {
+ case FILE_BROWSE_MODE_FILES:
+ return sfile->params;
+ case FILE_BROWSE_MODE_ASSETS:
+ return (FileSelectParams *)sfile->asset_params;
+ }
+
+ BLI_assert(!"Invalid browse mode set in file space.");
+ return NULL;
}
-FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile)
+FileSelectParams *ED_fileselect_get_file_params(const SpaceFile *sfile)
{
- if (!sfile->params) {
- fileselect_ensure_updated_file_params(sfile);
+ return (sfile->browse_mode == FILE_BROWSE_MODE_FILES) ? sfile->params : NULL;
+}
+
+FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL;
+}
+
+static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
+{
+ FileSelectAssetLibraryUID *library = &asset_params->asset_library;
+ FileSelectParams *base_params = &asset_params->base_params;
+ bUserAssetLibrary *user_library = NULL;
+
+ /* Ensure valid repo, or fall-back to local one. */
+ if (library->type == FILE_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;
+ }
+ }
+
+ switch (library->type) {
+ case FILE_ASSET_LIBRARY_LOCAL:
+ base_params->dir[0] = '\0';
+ break;
+ case FILE_ASSET_LIBRARY_CUSTOM:
+ BLI_assert(user_library);
+ BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
+ break;
}
- return sfile->params;
+ base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+}
+
+void fileselect_refresh_params(SpaceFile *sfile)
+{
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ if (asset_params) {
+ fileselect_refresh_asset_params(asset_params);
+ }
+}
+
+bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
/* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA
@@ -371,6 +490,8 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile)
wmOperator *op = sfile->op;
UserDef_FileSpaceData *sfile_udata = &U.file_space_data;
+ sfile->browse_mode = FILE_BROWSE_MODE_FILES;
+
FileSelectParams *params = fileselect_ensure_updated_file_params(sfile);
if (!op) {
return;
@@ -438,15 +559,6 @@ void ED_fileselect_params_to_userdef(SpaceFile *sfile,
}
}
-void ED_fileselect_reset_params(SpaceFile *sfile)
-{
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- params->type = FILE_UNIX;
- params->flag = 0;
- params->title[0] = '\0';
- params->active_file = -1;
-}
-
/**
* Sets FileSelectParams->file (name of selected file)
*/
@@ -1046,8 +1158,7 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil
sfile->op = NULL;
}
- folderlist_free(sfile->folders_prev);
- folderlist_free(sfile->folders_next);
+ folder_history_list_free(sfile);
if (sfile->files) {
ED_fileselect_clear(wm, owner_scene, sfile);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index c72ca58abba..774dc54700c 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -35,6 +35,8 @@
#include "BKE_screen.h"
#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_message.h"
@@ -55,6 +57,23 @@
#include "filelist.h"
#include "fsmenu.h"
+static ARegion *file_ui_region_ensure(ScrArea *area, ARegion *region_prev)
+{
+ ARegion *region;
+
+ if ((region = BKE_area_find_region_type(area, RGN_TYPE_UI)) != NULL) {
+ return region;
+ }
+
+ region = MEM_callocN(sizeof(ARegion), "execute region for file");
+ BLI_insertlinkafter(&area->regionbase, region_prev, region);
+ region->regiontype = RGN_TYPE_UI;
+ region->alignment = RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_DYNAMIC_SIZE;
+
+ return region;
+}
+
static ARegion *file_execute_region_ensure(ScrArea *area, ARegion *region_prev)
{
ARegion *region;
@@ -149,22 +168,10 @@ static void file_free(SpaceLink *sl)
sfile->files = NULL;
}
- if (sfile->folders_prev) {
- folderlist_free(sfile->folders_prev);
- MEM_freeN(sfile->folders_prev);
- sfile->folders_prev = NULL;
- }
+ folder_history_list_free(sfile);
- if (sfile->folders_next) {
- folderlist_free(sfile->folders_next);
- MEM_freeN(sfile->folders_next);
- sfile->folders_next = NULL;
- }
-
- if (sfile->params) {
- MEM_freeN(sfile->params);
- sfile->params = NULL;
- }
+ MEM_SAFE_FREE(sfile->params);
+ MEM_SAFE_FREE(sfile->asset_params);
if (sfile->layout) {
MEM_freeN(sfile->layout);
@@ -205,19 +212,20 @@ static SpaceLink *file_duplicate(SpaceLink *sl)
sfilen->previews_timer = NULL;
sfilen->smoothscroll_timer = NULL;
+ FileSelectParams *active_params_old = ED_fileselect_get_active_params(sfileo);
+ if (active_params_old) {
+ sfilen->files = filelist_new(active_params_old->type);
+ filelist_setdir(sfilen->files, active_params_old->dir);
+ }
+
if (sfileo->params) {
- sfilen->files = filelist_new(sfileo->params->type);
sfilen->params = MEM_dupallocN(sfileo->params);
- filelist_setdir(sfilen->files, sfilen->params->dir);
}
-
- if (sfileo->folders_prev) {
- sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev);
+ if (sfileo->asset_params) {
+ sfilen->asset_params = MEM_dupallocN(sfileo->asset_params);
}
- if (sfileo->folders_next) {
- sfilen->folders_next = folderlist_duplicate(sfileo->folders_next);
- }
+ sfilen->folder_histories = folder_history_list_duplicate(&sfileo->folder_histories);
if (sfileo->layout) {
sfilen->layout = MEM_dupallocN(sfileo->layout);
@@ -232,15 +240,30 @@ static void file_ensure_valid_region_state(bContext *C,
SpaceFile *sfile,
FileSelectParams *params)
{
- ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
- ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
- ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_tools = BKE_area_find_region_type(area, RGN_TYPE_TOOLS);
bool needs_init = false; /* To avoid multiple ED_area_init() calls. */
+ BLI_assert(region_tools);
+
+ if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) {
+ ARegion *region_execute = file_execute_region_ensure(area, region_tools);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
+
+ /* Hide specific regions by default. */
+ region_props->flag |= RGN_FLAG_HIDDEN;
+ region_execute->flag |= RGN_FLAG_HIDDEN;
+
+ ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
+ if (region_ui) {
+ ED_region_remove(C, area, region_ui);
+ needs_init = true;
+ }
+ }
/* If there's an file-operation, ensure we have the option and execute region */
- if (sfile->op && (region_props == NULL)) {
- region_execute = file_execute_region_ensure(area, region_ui);
- region_props = file_tool_props_region_ensure(area, region_execute);
+ else if (sfile->op) {
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ ARegion *region_execute = file_execute_region_ensure(area, region_ui);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
if (params->flag & FILE_HIDE_TOOL_PROPS) {
region_props->flag |= RGN_FLAG_HIDDEN;
@@ -252,12 +275,19 @@ static void file_ensure_valid_region_state(bContext *C,
needs_init = true;
}
/* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */
- else if ((sfile->op == NULL) && (region_props != NULL)) {
- BLI_assert(region_execute != NULL);
+ else {
+ ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
+ ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ UNUSED_VARS(region_ui);
- ED_region_remove(C, area, region_props);
- ED_region_remove(C, area, region_execute);
- needs_init = true;
+ if (region_props) {
+ BLI_assert(region_execute);
+
+ ED_region_remove(C, area, region_props);
+ ED_region_remove(C, area, region_execute);
+ needs_init = true;
+ }
}
if (needs_init) {
@@ -265,24 +295,42 @@ static void file_ensure_valid_region_state(bContext *C,
}
}
+/**
+ * Tag the space to recreate the file-list.
+ */
+static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile)
+{
+ filelist_tag_force_reset(sfile->files);
+ ED_area_tag_refresh(area);
+}
+
static void file_refresh(const bContext *C, ScrArea *area)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_ensure_active_params(sfile);
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
struct FSMenu *fsmenu = ED_fsmenu_get();
- if (!sfile->folders_prev) {
- sfile->folders_prev = folderlist_new();
+ fileselect_refresh_params(sfile);
+ folder_history_list_ensure_for_active_browse_mode(sfile);
+
+ if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) &&
+ filelist_needs_reset_on_main_changes(sfile->files)) {
+ filelist_tag_force_reset(sfile->files);
}
+ sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES;
+
if (!sfile->files) {
sfile->files = filelist_new(params->type);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
+ filelist_settype(sfile->files, params->type);
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
+ filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL);
filelist_setfilter_options(
sfile->files,
(params->flag & FILE_FILTER) != 0,
@@ -290,6 +338,7 @@ static void file_refresh(const bContext *C, ScrArea *area)
true, /* Just always hide parent, prefer to not add an extra user option for this. */
params->filter,
params->filter_id,
+ (params->flag & FILE_ASSETS_ONLY) != 0,
params->filter_glob,
params->filter_search);
@@ -300,12 +349,12 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
- if (filelist_force_reset(sfile->files)) {
+ if (filelist_needs_force_reset(sfile->files)) {
filelist_readjob_stop(wm, CTX_data_scene(C));
filelist_clear(sfile->files);
}
- if (filelist_empty(sfile->files)) {
+ if (filelist_needs_reading(sfile->files)) {
if (!filelist_pending(sfile->files)) {
filelist_readjob_start(sfile->files, C);
}
@@ -363,8 +412,21 @@ static void file_listener(wmWindow *UNUSED(win),
ED_area_tag_refresh(area);
}
break;
+ case ND_SPACE_ASSET_PARAMS:
+ if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) {
+ ED_area_tag_refresh(area);
+ }
+ break;
}
break;
+ case NC_ASSET: {
+ if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
+ /* 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. */
+ file_tag_reset_list(area, sfile);
+ }
+ break;
+ }
}
}
@@ -442,6 +504,22 @@ static void file_main_region_message_subscribe(const struct bContext *UNUSED(C),
}
}
+static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile)
+{
+ /* Needed, because filelist is not initialized on loading */
+ if (!sfile->files || filelist_needs_reading(sfile->files)) {
+ return true;
+ }
+
+ /* File reading tagged the space because main data changed that may require a filelist reset. */
+ if (filelist_needs_reset_on_main_changes(sfile->files) &&
+ (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES)) {
+ return true;
+ }
+
+ return false;
+}
+
static void file_main_region_draw(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
@@ -450,8 +528,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
View2D *v2d = &region->v2d;
- /* Needed, because filelist is not initialized on loading */
- if (!sfile->files || filelist_empty(sfile->files)) {
+ if (file_main_region_needs_refresh_before_draw(sfile)) {
file_refresh(C, NULL);
}
@@ -497,7 +574,9 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
file_highlight_set(sfile, region, event->x, event->y);
}
- file_draw_list(C, region);
+ if (!file_draw_hint_if_invalid(sfile, region)) {
+ file_draw_list(C, region);
+ }
/* reset view matrix */
UI_view2d_view_restore(C);
@@ -681,6 +760,87 @@ static void file_dropboxes(void)
WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy);
}
+static int file_space_subtype_get(ScrArea *area)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ return sfile->browse_mode;
+}
+
+static void file_space_subtype_set(ScrArea *area, int value)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ sfile->browse_mode = value;
+}
+
+static void file_space_subtype_item_extend(bContext *UNUSED(C),
+ EnumPropertyItem **item,
+ int *totitem)
+{
+ RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
+}
+
+static const char *file_context_dir[] = {"active_file", "active_id", NULL};
+
+static int /*eContextResult*/ file_context(const bContext *C,
+ const char *member,
+ bContextDataResult *result)
+{
+ bScreen *screen = CTX_wm_screen(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ BLI_assert(!ED_area_is_global(CTX_wm_area(C)));
+
+ if (CTX_data_dir(member)) {
+ CTX_data_dir_set(result, file_context_dir);
+ return CTX_RESULT_OK;
+ }
+
+ /* The following member checks return file-list data, check if that needs refreshing first. */
+ if (file_main_region_needs_refresh_before_draw(sfile)) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ if (CTX_data_equals(member, "active_file")) {
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
+ if (file == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
+ return CTX_RESULT_OK;
+ }
+ if (CTX_data_equals(member, "id")) {
+ const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
+ if (file == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ ID *id = filelist_file_get_id(file);
+ if (id == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_id_pointer_set(result, id);
+ return CTX_RESULT_OK;
+ }
+
+ return CTX_RESULT_MEMBER_NOT_FOUND;
+}
+
+static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id))
+{
+ SpaceFile *sfile = (SpaceFile *)sl;
+
+ /* If the file shows main data (IDs), tag it for reset. */
+ if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
+ /* Full refresh 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. */
+ file_tag_reset_list(area, sfile);
+ }
+}
+
/* only called once, from space/spacetypes.c */
void ED_spacetype_file(void)
{
@@ -700,6 +860,11 @@ void ED_spacetype_file(void)
st->operatortypes = file_operatortypes;
st->keymap = file_keymap;
st->dropboxes = file_dropboxes;
+ st->space_subtype_item_extend = file_space_subtype_item_extend;
+ st->space_subtype_get = file_space_subtype_get;
+ st->space_subtype_set = file_space_subtype_set;
+ st->context = file_context;
+ st->id_remap = file_id_remap;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt
index 414e5c87f5a..2a795dd954c 100644
--- a/source/blender/editors/space_graph/CMakeLists.txt
+++ b/source/blender/editors/space_graph/CMakeLists.txt
@@ -34,8 +34,8 @@ set(SRC
graph_draw.c
graph_edit.c
graph_ops.c
- graph_slider_ops.c
graph_select.c
+ graph_slider_ops.c
graph_utils.c
graph_view.c
space_graph.c
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index c3e4eceef6e..e56d71913d6 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -2354,12 +2354,12 @@ static const EnumPropertyItem prop_graphkeys_mirror_types[] = {
{GRAPHKEYS_MIRROR_YAXIS,
"YAXIS",
0,
- "By Times Over Time=0",
+ "By Times Over Zero Time",
"Flip times of selected keyframes, effectively reversing the order they appear in"},
{GRAPHKEYS_MIRROR_XAXIS,
"XAXIS",
0,
- "By Values Over Value=0",
+ "By Values Over Zero Value",
"Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
{GRAPHKEYS_MIRROR_MARKER,
"MARKER",
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 12035ab6b61..4ab4ef518fb 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -1288,8 +1288,8 @@ void GRAPH_OT_select_less(wmOperatorType *ot)
/* defines for left-right select tool */
static const EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
{GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
- {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
- {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
+ {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c
index 0b9ba3762a3..cde0dab3503 100644
--- a/source/blender/editors/space_graph/graph_view.c
+++ b/source/blender/editors/space_graph/graph_view.c
@@ -533,4 +533,4 @@ void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot)
/* Flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-} \ No newline at end of file
+}
diff --git a/source/blender/editors/space_info/info_report.c b/source/blender/editors/space_info/info_report.c
index 3ba088018c3..7dd8382c8ef 100644
--- a/source/blender/editors/space_info/info_report.c
+++ b/source/blender/editors/space_info/info_report.c
@@ -396,7 +396,7 @@ void INFO_OT_report_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Reports to Clipboard";
- ot->description = "Copy selected reports to Clipboard";
+ ot->description = "Copy selected reports to clipboard";
ot->idname = "INFO_OT_report_copy";
/* api callbacks */
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index d8f31161c20..f4a3bb96aeb 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -21,6 +21,7 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
+ ../../compositor
../../depsgraph
../../draw
../../gpu
@@ -29,7 +30,6 @@ set(INC
../../makesrna
../../nodes
../../render
- ../../compositor
../../windowmanager
../../../../intern/glew-mx
../../../../intern/guardedalloc
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 84e7a74fab3..45f3b6cf9c9 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -3182,6 +3182,46 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
+static void node_geometry_buts_point_instance(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "instance_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_fill(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
+ // uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_mix(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE);
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE);
+ uiItemR(col, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("A"), ICON_NONE);
+ uiItemR(col, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("B"), ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_point_distribute(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_color_ramp(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiTemplateColorRamp(layout, ptr, "color_ramp", 0);
+}
+
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@@ -3194,12 +3234,27 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_TRIANGULATE:
ntype->draw_buttons = node_geometry_buts_triangulate;
break;
- case GEO_NODE_RANDOM_ATTRIBUTE:
+ case GEO_NODE_ATTRIBUTE_RANDOMIZE:
ntype->draw_buttons = node_geometry_buts_random_attribute;
break;
case GEO_NODE_ATTRIBUTE_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_math;
break;
+ case GEO_NODE_POINT_INSTANCE:
+ ntype->draw_buttons = node_geometry_buts_point_instance;
+ break;
+ case GEO_NODE_ATTRIBUTE_FILL:
+ ntype->draw_buttons = node_geometry_buts_attribute_fill;
+ break;
+ case GEO_NODE_ATTRIBUTE_MIX:
+ ntype->draw_buttons = node_geometry_buts_attribute_mix;
+ break;
+ case GEO_NODE_POINT_DISTRIBUTE:
+ ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
+ break;
+ case GEO_NODE_ATTRIBUTE_COLOR_RAMP:
+ ntype->draw_buttons = node_geometry_buts_attribute_color_ramp;
+ break;
}
}
@@ -3389,6 +3444,7 @@ static const float std_node_socket_colors[][4] = {
{0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */
{0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */
{0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */
+ {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
};
/* common color callbacks for standard types */
@@ -3512,6 +3568,10 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_COLLECTION: {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index fc7fa3a6caa..5a2eb0cc3a0 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -344,8 +344,6 @@ void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry)
/* based on settings in node, sets drawing rect info. each redraw! */
static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
{
- uiLayout *layout, *row;
-
PointerRNA nodeptr;
RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
@@ -374,15 +372,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- NODE_WIDTH(node) - NODE_DY,
- NODE_DY,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ NODE_WIDTH(node) - NODE_DY,
+ NODE_DY,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -393,7 +391,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "socket", &sockptr);
/* align output buttons to the right */
- row = uiLayoutRow(layout, 1);
+ uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
@@ -469,15 +467,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
node->butr.ymin = 0;
node->butr.ymax = 0;
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- node->butr.xmax,
- 0,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ node->butr.xmax,
+ 0,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -502,15 +500,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- NODE_WIDTH(node) - NODE_DY,
- NODE_DY,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ NODE_WIDTH(node) - NODE_DY,
+ NODE_DY,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -520,7 +518,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
- row = uiLayoutRow(layout, 1);
+ uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
@@ -771,7 +769,6 @@ void node_socket_color_get(
bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4])
{
PointerRNA ptr;
-
BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node));
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index 039ddad71ef..fdce5e3f30b 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -100,9 +100,7 @@ typedef struct CompoJob {
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
{
- bNode *node;
-
- for (node = nodetree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
if (node->type == CMP_NODE_COMPOSITE) {
if (recalc_flags & COM_RECALC_COMPOSITE) {
node->flag |= NODE_DO_OUTPUT_RECALC;
@@ -124,14 +122,12 @@ static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
static int compo_get_recalc_flags(const bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win;
int recalc_flags = 0;
- for (win = wm->windows.first; win; win = win->next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
- ScrArea *area;
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_IMAGE) {
SpaceImage *sima = area->spacedata.first;
if (sima->image) {
@@ -247,7 +243,6 @@ static void compo_startjob(void *cjv,
CompoJob *cj = cjv;
bNodeTree *ntree = cj->localtree;
Scene *scene = cj->scene;
- SceneRenderView *srv;
if (scene->use_nodes == false) {
return;
@@ -280,7 +275,7 @@ static void compo_startjob(void *cjv,
"");
}
else {
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) {
continue;
}
@@ -309,8 +304,6 @@ static void compo_startjob(void *cjv,
*/
void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
{
- wmJob *wm_job;
- CompoJob *cj;
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -327,13 +320,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
BKE_image_backup_render(
scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false);
- wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- scene_owner,
- "Compositing",
- WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
- WM_JOB_TYPE_COMPOSITE);
- cj = MEM_callocN(sizeof(CompoJob), "compo job");
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ scene_owner,
+ "Compositing",
+ WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
+ WM_JOB_TYPE_COMPOSITE);
+ CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job");
/* customdata for preview thread */
cj->bmain = bmain;
@@ -524,9 +517,6 @@ void ED_node_shader_default(const bContext *C, ID *id)
/* called from shading buttons or header */
void ED_node_composit_default(const bContext *C, struct Scene *sce)
{
- bNode *in, *out;
- bNodeSocket *fromsock, *tosock;
-
/* but lets check it anyway */
if (sce->nodetree) {
if (G.debug & G_DEBUG) {
@@ -541,18 +531,18 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
sce->nodetree->render_quality = NTREE_QUALITY_HIGH;
- out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE);
+ bNode *out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE);
out->locx = 300.0f;
out->locy = 400.0f;
- in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS);
+ bNode *in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS);
in->locx = 10.0f;
in->locy = 400.0f;
nodeSetActive(sce->nodetree, in);
/* links from color to color */
- fromsock = in->outputs.first;
- tosock = out->inputs.first;
+ bNodeSocket *fromsock = in->outputs.first;
+ bNodeSocket *tosock = out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
@@ -562,9 +552,6 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
/* called from shading buttons or header */
void ED_node_texture_default(const bContext *C, Tex *tex)
{
- bNode *in, *out;
- bNodeSocket *fromsock, *tosock;
-
/* but lets check it anyway */
if (tex->nodetree) {
if (G.debug & G_DEBUG) {
@@ -575,17 +562,17 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname);
- out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
+ bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
out->locx = 300.0f;
out->locy = 300.0f;
- in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER);
+ bNode *in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER);
in->locx = 10.0f;
in->locy = 300.0f;
nodeSetActive(tex->nodetree, in);
- fromsock = in->outputs.first;
- tosock = out->inputs.first;
+ bNodeSocket *fromsock = in->outputs.first;
+ bNodeSocket *tosock = out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), tex->nodetree);
@@ -634,15 +621,13 @@ void snode_set_context(const bContext *C)
void snode_update(SpaceNode *snode, bNode *node)
{
- bNodeTreePath *path;
-
/* XXX this only updates nodes in the current node space tree path.
* The function supposedly should update any potential group node linking to changed tree,
* this really requires a working depsgraph ...
*/
/* update all edited group nodes */
- path = snode->treepath.last;
+ bNodeTreePath *path = snode->treepath.last;
if (path) {
bNodeTree *ngroup = path->nodetree;
for (path = path->prev; path; path = path->prev) {
@@ -671,10 +656,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
/* generic node group output: set node as active output */
if (node->type == NODE_GROUP_OUTPUT) {
- bNode *tnode;
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == NODE_GROUP_OUTPUT) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == NODE_GROUP_OUTPUT) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -696,11 +680,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
SH_NODE_OUTPUT_WORLD,
SH_NODE_OUTPUT_LIGHT,
SH_NODE_OUTPUT_LINESTYLE)) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == node->type) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == node->type) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -715,16 +697,13 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
/* if active texture changed, free glsl materials */
if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) {
- Material *ma;
- World *wo;
-
- for (ma = bmain->materials.first; ma; ma = ma->id.next) {
+ LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) {
GPU_material_free(&ma->gpumaterial);
}
}
- for (wo = bmain->worlds.first; wo; wo = wo->id.next) {
+ LISTBASE_FOREACH (World *, wo, &bmain->materials) {
if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree)) {
GPU_material_free(&wo->gpumaterial);
}
@@ -742,11 +721,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
else if (ntree->type == NTREE_COMPOSIT) {
/* make active viewer, currently only 1 supported... */
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (ELEM(node_iter->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -760,11 +737,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
}
else if (node->type == CMP_NODE_COMPOSITE) {
if (was_output == 0) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == CMP_NODE_COMPOSITE) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == CMP_NODE_COMPOSITE) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -879,14 +854,12 @@ static void edit_node_properties_get(
/* is rct in visible part of node? */
static bNode *visible_node(SpaceNode *snode, const rctf *rct)
{
- bNode *node;
-
- for (node = snode->edittree->nodes.last; node; node = node->prev) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (BLI_rctf_isect(&node->totr, rct, NULL)) {
- break;
+ return node;
}
}
- return node;
+ return NULL;
}
/* ********************** size widget operator ******************** */
@@ -952,23 +925,19 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
ARegion *region = CTX_wm_region(C);
bNode *node = nodeGetActive(snode->edittree);
NodeSizeWidget *nsw = op->customdata;
- float mx, my, dx, dy;
switch (event->type) {
- case MOUSEMOVE:
-
+ case MOUSEMOVE: {
+ float mx, my;
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &mx, &my);
- dx = (mx - nsw->mxstart) / UI_DPI_FAC;
- dy = (my - nsw->mystart) / UI_DPI_FAC;
+ float dx = (mx - nsw->mxstart) / UI_DPI_FAC;
+ float dy = (my - nsw->mystart) / UI_DPI_FAC;
if (node) {
- float *pwidth;
- float oldwidth, widthmin, widthmax;
-
- pwidth = &node->width;
- oldwidth = nsw->oldwidth;
- widthmin = node->typeinfo->minwidth;
- widthmax = node->typeinfo->maxwidth;
+ float *pwidth = &node->width;
+ float oldwidth = nsw->oldwidth;
+ float widthmin = node->typeinfo->minwidth;
+ float widthmax = node->typeinfo->maxwidth;
{
if (nsw->directions & NODE_RESIZE_RIGHT) {
@@ -1026,23 +995,24 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_region_tag_redraw(region);
break;
-
+ }
case LEFTMOUSE:
case MIDDLEMOUSE:
- case RIGHTMOUSE:
+ case RIGHTMOUSE: {
if (event->val == KM_RELEASE) {
node_resize_exit(C, op, false);
ED_node_post_apply_transform(C, snode->edittree);
return OPERATOR_FINISHED;
}
- else if (event->val == KM_PRESS) {
+ if (event->val == KM_PRESS) {
node_resize_exit(C, op, true);
ED_region_tag_redraw(region);
return OPERATOR_CANCELLED;
}
break;
+ }
}
return OPERATOR_RUNNING_MODAL;
@@ -1095,14 +1065,12 @@ void NODE_OT_resize(wmOperatorType *ot)
bool node_has_hidden_sockets(bNode *node)
{
- bNodeSocket *sock;
-
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (sock->flag & SOCK_HIDDEN) {
return true;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (sock->flag & SOCK_HIDDEN) {
return true;
}
@@ -1112,24 +1080,22 @@ bool node_has_hidden_sockets(bNode *node)
void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
{
- bNodeSocket *sock;
-
if (set == 0) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
sock->flag &= ~SOCK_HIDDEN;
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
sock->flag &= ~SOCK_HIDDEN;
}
}
else {
/* hide unused sockets */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (sock->link == NULL) {
sock->flag |= SOCK_HIDDEN;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (nodeCountSocketLinks(snode->edittree, sock) == 0) {
sock->flag |= SOCK_HIDDEN;
}
@@ -1142,16 +1108,13 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
int node_find_indicated_socket(
SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
{
- bNode *node;
- bNodeSocket *sock;
rctf rect;
*nodep = NULL;
*sockp = NULL;
/* check if we click in a socket */
- for (node = snode->edittree->nodes.first; node; node = node->next) {
-
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4);
if (!(node->flag & NODE_HIDDEN)) {
@@ -1167,7 +1130,7 @@ int node_find_indicated_socket(
}
if (in_out & SOCK_IN) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
@@ -1180,7 +1143,7 @@ int node_find_indicated_socket(
}
}
if (in_out & SOCK_OUT) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
@@ -1226,17 +1189,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node, *newnode, *lastnode;
- bNodeLink *link, *newlink, *lastlink;
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- lastnode = ntree->nodes.last;
- for (node = ntree->nodes.first; node; node = node->next) {
+ bNode *lastnode = ntree->nodes.last;
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
- newnode = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
+ BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
/* to ensure redraws or rerenders happen */
ED_node_tag_update_id(snode->id);
@@ -1251,14 +1212,14 @@ 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!
*/
- lastlink = ntree->links.last;
- for (link = ntree->links.first; link; link = link->next) {
+ bNodeLink *lastlink = ntree->links.last;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes.
* If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)!
*/
if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
(keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) {
- newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -1282,11 +1243,11 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
/* clear flags for recursive depth-first iteration */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->flag &= ~NODE_TEST;
}
/* reparent copied nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) {
node_duplicate_reparent_recursive(node);
}
@@ -1298,10 +1259,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
/* deselect old nodes, select the copies instead */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* has been set during copy above */
- newnode = node->new_node;
+ bNode *newnode = node->new_node;
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
@@ -1389,17 +1350,16 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- Scene *curscene = CTX_data_scene(C), *scene;
- bNode *node;
+ Scene *curscene = CTX_data_scene(C);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
/* first tag scenes unread */
- for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->id.tag |= LIB_TAG_DOIT;
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->type == CMP_NODE_R_LAYERS) {
ID *id = node->id;
if (id->tag & LIB_TAG_DOIT) {
@@ -1434,13 +1394,14 @@ void NODE_OT_read_viewlayers(wmOperatorType *ot)
int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *sce = CTX_data_scene(C);
- bNode *node;
/* This is actually a test whether scene is used by the compositor or not.
* All the nodes are using same render result, so there is no need to do
* anything smart about check how exactly scene is used. */
- for (node = sce->nodetree->nodes.first; node; node = node->next) {
- if (node->id == (ID *)sce) {
+ bNode *node = NULL;
+ LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) {
+ if (node_iter->id == (ID *)sce) {
+ node = node_iter;
break;
}
}
@@ -1486,14 +1447,14 @@ void NODE_OT_render_changed(wmOperatorType *ot)
static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
{
- bNode *node;
int tot_eq = 0, tot_neq = 0;
/* Toggles the flag on all selected nodes.
* If the flag is set on all nodes it is unset.
* If the flag is not set on all nodes, it is set.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
@@ -1512,7 +1473,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
}
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
@@ -1631,8 +1592,6 @@ void NODE_OT_options_toggle(wmOperatorType *ot)
static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- int hidden;
/* sanity checking (poll callback checks this already) */
if ((snode == NULL) || (snode->edittree == NULL)) {
@@ -1642,17 +1601,17 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
/* Toggle for all selected nodes */
- hidden = 0;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ bool hidden = false;
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (node_has_hidden_sockets(node)) {
- hidden = 1;
+ hidden = true;
break;
}
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
node_set_hidden_sockets(snode, node, !hidden);
}
@@ -1686,12 +1645,11 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
/* Only allow muting of nodes having a mute func! */
if ((node->flag & SELECT) && node->typeinfo->update_internal_links) {
node->flag ^= NODE_MUTED;
@@ -1731,13 +1689,11 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node));
nodeRemoveNode(bmain, snode->edittree, node, true);
@@ -1787,10 +1743,8 @@ static bool node_switch_view_poll(bContext *C)
static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
/* call the update function from the Switch View node */
node->update = NODE_UPDATE_OPERATOR;
@@ -1825,12 +1779,10 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
nodeInternalRelink(snode->edittree, node);
nodeRemoveNode(bmain, snode->edittree, node, true);
@@ -1963,9 +1915,6 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNode *node = NULL;
- NodeImageMultiFile *nimf;
- bNodeSocket *sock;
- int direction;
if (ptr.data) {
node = ptr.data;
@@ -1978,14 +1927,14 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- nimf = node->storage;
+ NodeImageMultiFile *nimf = node->storage;
- sock = BLI_findlink(&node->inputs, nimf->active_input);
+ bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
if (!sock) {
return OPERATOR_CANCELLED;
}
- direction = RNA_enum_get(op->ptr, "direction");
+ int direction = RNA_enum_get(op->ptr, "direction");
if (direction == 1) {
bNodeSocket *before = sock->prev;
@@ -2037,24 +1986,23 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node, *tnode;
if (!ntree) {
return OPERATOR_CANCELLED;
}
- node = nodeGetActive(ntree);
+ bNode *node = nodeGetActive(ntree);
if (!node) {
return OPERATOR_CANCELLED;
}
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->flag & NODE_SELECT && tnode != node) {
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->flag & NODE_SELECT && node_iter != node) {
if (node->flag & NODE_CUSTOM_COLOR) {
- tnode->flag |= NODE_CUSTOM_COLOR;
- copy_v3_v3(tnode->color, node->color);
+ node_iter->flag |= NODE_CUSTOM_COLOR;
+ copy_v3_v3(node_iter->color, node->color);
}
else {
- tnode->flag &= ~NODE_CUSTOM_COLOR;
+ node_iter->flag &= ~NODE_CUSTOM_COLOR;
}
}
}
@@ -2086,8 +2034,6 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node;
- bNodeLink *link, *newlink;
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
@@ -2095,7 +2041,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
BKE_node_clipboard_clear();
BKE_node_clipboard_init(ntree);
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
@@ -2105,7 +2051,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
bNode *new_node = node->new_node;
@@ -2126,11 +2072,11 @@ 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!
*/
- for (link = ntree->links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
(link->fromnode->flag & NODE_SELECT)) {
- newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -2165,18 +2111,11 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- const ListBase *clipboard_nodes_lb;
- const ListBase *clipboard_links_lb;
- bNode *node;
- bNodeLink *link;
- int num_nodes;
- float center[2];
- bool is_clipboard_valid, all_nodes_valid;
/* validate pointers in the clipboard */
- is_clipboard_valid = BKE_node_clipboard_validate();
- clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
- clipboard_links_lb = BKE_node_clipboard_get_links();
+ bool is_clipboard_valid = BKE_node_clipboard_validate();
+ const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
+ const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links();
if (BLI_listbase_is_empty(clipboard_nodes_lb)) {
BKE_report(op->reports, RPT_ERROR, "Clipboard is empty");
@@ -2196,8 +2135,8 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
/* make sure all clipboard nodes would be valid in the target tree */
- all_nodes_valid = true;
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ bool all_nodes_valid = true;
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
all_nodes_valid = false;
BKE_reportf(op->reports,
@@ -2217,15 +2156,16 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
node_deselect_all(snode);
/* calculate "barycenter" for placing on mouse cursor */
- zero_v2(center);
- for (node = clipboard_nodes_lb->first, num_nodes = 0; node; node = node->next, num_nodes++) {
+ float center[2] = {0.0f, 0.0f};
+ int num_nodes = 0;
+ LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) {
center[0] += BLI_rctf_cent_x(&node->totr);
center[1] += BLI_rctf_cent_y(&node->totr);
}
mul_v2_fl(center, 1.0 / num_nodes);
/* copy nodes from clipboard */
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
/* pasted nodes are selected */
@@ -2233,14 +2173,14 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
/* reparent copied nodes */
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = node->new_node;
if (new_node->parent) {
new_node->parent = new_node->parent->new_node;
}
}
- for (link = clipboard_links_lb->first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
nodeAddLink(ntree,
link->fromnode->new_node,
link->fromsock->new_sock,
@@ -2275,10 +2215,9 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot)
static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
{
- bNodeSocket *sock;
- for (sock = lb->first; sock; sock = sock->next) {
- if (sock->flag & SELECT) {
- return sock;
+ LISTBASE_FOREACH (bNodeSocket *, socket, lb) {
+ if (socket->flag & SELECT) {
+ return socket;
}
}
return NULL;
@@ -2289,12 +2228,12 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
int in_out = RNA_enum_get(op->ptr, "in_out");
- PointerRNA ntree_ptr;
- bNodeSocket *sock, *tsock, *active_sock;
- const char *default_name;
+ PointerRNA ntree_ptr;
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
+ const char *default_name;
+ bNodeSocket *active_sock;
if (in_out == SOCK_IN) {
active_sock = ntree_get_active_interface_socket(&ntree->inputs);
default_name = "Input";
@@ -2304,6 +2243,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
default_name = "Output";
}
+ bNodeSocket *sock;
if (active_sock) {
/* insert a copy of the active socket right after it */
sock = ntreeInsertSocketInterface(
@@ -2317,11 +2257,11 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
}
/* deactivate sockets (has to check both lists) */
- for (tsock = ntree->inputs.first; tsock; tsock = tsock->next) {
- tsock->flag &= ~SELECT;
+ LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->inputs) {
+ socket_iter->flag &= ~SELECT;
}
- for (tsock = ntree->outputs.first; tsock; tsock = tsock->next) {
- tsock->flag &= ~SELECT;
+ LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->outputs) {
+ socket_iter->flag &= ~SELECT;
}
/* make the new socket active */
sock->flag |= SELECT;
@@ -2359,9 +2299,8 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeSocket *iosock, *active_sock;
- iosock = ntree_get_active_interface_socket(&ntree->inputs);
+ bNodeSocket *iosock = ntree_get_active_interface_socket(&ntree->inputs);
if (!iosock) {
iosock = ntree_get_active_interface_socket(&ntree->outputs);
}
@@ -2370,7 +2309,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
}
/* preferably next socket becomes active, otherwise try previous socket */
- active_sock = (iosock->next ? iosock->next : iosock->prev);
+ bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev);
ntreeRemoveSocketInterface(ntree, iosock);
/* set active socket */
@@ -2416,11 +2355,9 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
int direction = RNA_enum_get(op->ptr, "direction");
- bNodeSocket *iosock;
- ListBase *lb;
- lb = &ntree->inputs;
- iosock = ntree_get_active_interface_socket(lb);
+ ListBase *lb = &ntree->inputs;
+ bNodeSocket *iosock = ntree_get_active_interface_socket(lb);
if (!iosock) {
lb = &ntree->outputs;
iosock = ntree_get_active_interface_socket(lb);
@@ -2489,8 +2426,6 @@ static bool node_shader_script_update_poll(bContext *C)
Scene *scene = CTX_data_scene(C);
const RenderEngineType *type = RE_engines_find(scene->r.engine);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- Text *text;
/* test if we have a render engine that supports shaders scripts */
if (!(type && type->update_script_node)) {
@@ -2498,7 +2433,7 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a shader script node in context */
- node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
+ bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
if (!node && snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
@@ -2513,7 +2448,7 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a text datablock in context */
- text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
return 1;
}
@@ -2530,12 +2465,11 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine,
Text *text)
{
bool found = false;
- bNode *node;
ntree->done = true;
/* update each script that is using this text datablock */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == NODE_GROUP) {
bNodeTree *ngroup = (bNodeTree *)node->id;
if (ngroup && !ngroup->done) {
@@ -2557,18 +2491,16 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript);
- bNodeTree *ntree_base = NULL;
- bNode *node = NULL;
- RenderEngine *engine;
- RenderEngineType *type;
bool found = false;
/* setup render engine */
- type = RE_engines_find(scene->r.engine);
- engine = RE_engine_create(type);
+ RenderEngineType *type = RE_engines_find(scene->r.engine);
+ RenderEngine *engine = RE_engine_create(type);
engine->reports = op->reports;
/* get node */
+ bNodeTree *ntree_base = NULL;
+ bNode *node = NULL;
if (nodeptr.data) {
ntree_base = (bNodeTree *)nodeptr.owner_id;
node = nodeptr.data;
@@ -2643,10 +2575,8 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode,
float *fx,
float *fy)
{
- float bufx, bufy;
-
- bufx = backdrop_width * snode->zoom;
- bufy = backdrop_height * snode->zoom;
+ float bufx = backdrop_width * snode->zoom;
+ float bufy = backdrop_height * snode->zoom;
*fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
*fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
@@ -2655,14 +2585,12 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode,
static int viewer_border_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Image *ima;
void *lock;
- ImBuf *ibuf;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
if (ibuf) {
ARegion *region = CTX_wm_region(C);
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c
index 3c861896d37..d2c88ed787c 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.c
@@ -283,7 +283,7 @@ void NODE_OT_backimage_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Background Image Move";
- ot->description = "Move Node backdrop";
+ ot->description = "Move node backdrop";
ot->idname = "NODE_OT_backimage_move";
/* api callbacks */
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index afc1a963f4f..ad7632377a3 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -645,7 +645,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C),
/* rule might not work? */
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- return WM_drag_ID(drag, ID_IM) != NULL;
+ return WM_drag_get_local_ID(drag, ID_IM) != NULL;
}
static bool node_mask_drop_poll(bContext *UNUSED(C),
@@ -653,19 +653,19 @@ static bool node_mask_drop_poll(bContext *UNUSED(C),
const wmEvent *UNUSED(event),
const char **UNUSED(r_tooltip))
{
- return WM_drag_ID(drag, ID_MSK) != NULL;
+ return WM_drag_get_local_ID(drag, ID_MSK) != NULL;
}
static void node_id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void node_id_path_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
if (id) {
RNA_string_set(drop->ptr, "name", id->name + 2);
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 74f99540bee..e0262371559 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -34,6 +34,7 @@ set(INC
set(SRC
outliner_collections.c
+ outliner_context.c
outliner_dragdrop.c
outliner_draw.c
outliner_edit.c
@@ -46,12 +47,12 @@ set(SRC
space_outliner.c
tree/common.cc
tree/tree_display.cc
+ tree/tree_display_data.cc
tree/tree_display_libraries.cc
- tree/tree_display_view_layer.cc
- tree/tree_display_sequencer.cc
tree/tree_display_orphaned.cc
tree/tree_display_scenes.cc
- tree/tree_display_data.cc
+ tree/tree_display_sequencer.cc
+ tree/tree_display_view_layer.cc
tree/tree_element.cc
tree/tree_element_anim_data.cc
tree/tree_element_driver_base.cc
diff --git a/source/blender/editors/space_outliner/outliner_context.c b/source/blender/editors/space_outliner/outliner_context.c
new file mode 100644
index 00000000000..a314a640e42
--- /dev/null
+++ b/source/blender/editors/space_outliner/outliner_context.c
@@ -0,0 +1,73 @@
+/*
+ * 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) 2017 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "BLI_listbase.h"
+
+#include "BKE_context.h"
+
+#include "DNA_space_types.h"
+
+#include "RNA_access.h"
+
+#include "outliner_intern.h"
+
+static void outliner_context_selected_ids_recursive(const ListBase *subtree,
+ bContextDataResult *result)
+{
+ LISTBASE_FOREACH (const TreeElement *, te, subtree) {
+ const TreeStoreElem *tse = TREESTORE(te);
+ if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, 0, TSE_LAYER_COLLECTION))) {
+ CTX_data_id_list_add(result, tse->id);
+ }
+ outliner_context_selected_ids_recursive(&te->subtree, result);
+ }
+}
+
+static void outliner_context_selected_ids(const SpaceOutliner *space_outliner,
+ bContextDataResult *result)
+{
+ outliner_context_selected_ids_recursive(&space_outliner->tree, result);
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+}
+
+static const char *outliner_context_dir[] = {"selected_ids", NULL};
+
+int /*eContextResult*/ outliner_context(const bContext *C,
+ const char *member,
+ bContextDataResult *result)
+{
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+
+ if (CTX_data_dir(member)) {
+ CTX_data_dir_set(result, outliner_context_dir);
+ return CTX_RESULT_OK;
+ }
+ if (CTX_data_equals(member, "selected_ids")) {
+ outliner_context_selected_ids(space_outliner, result);
+ return CTX_RESULT_OK;
+ }
+ /* Note: Querying non-ID selection could also work if tree elements stored their matching RNA
+ * struct type. */
+
+ return CTX_RESULT_MEMBER_NOT_FOUND;
+}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index d3da7b80765..152bbc96281 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -333,7 +333,7 @@ static bool parent_drop_poll(bContext *C,
ED_region_tag_redraw_no_rebuild(CTX_wm_region(C));
}
- Object *potential_child = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *potential_child = (Object *)WM_drag_get_local_ID(drag, ID_OB);
if (!potential_child) {
return false;
}
@@ -421,7 +421,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
Object *par = (Object *)tselem->id;
- Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB);
if (ELEM(NULL, ob, par)) {
return OPERATOR_CANCELLED;
@@ -445,7 +445,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void OUTLINER_OT_parent_drop(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Drop to Set Parent [+Alt keeps transforms]";
+ ot->name = "Drop to Set Parent (hold Alt to keep transforms)";
ot->description = "Drag to parent in Outliner";
ot->idname = "OUTLINER_OT_parent_drop";
@@ -473,7 +473,7 @@ static bool parent_clear_poll(bContext *C,
}
}
- Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
if (!ob) {
return false;
}
@@ -531,7 +531,7 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven
void OUTLINER_OT_parent_clear(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Drop to Clear Parent [+Alt keeps transforms]";
+ ot->name = "Drop to Clear Parent (hold Alt to keep transforms)";
ot->description = "Drag to clear parent in Outliner";
ot->idname = "OUTLINER_OT_parent_clear";
@@ -552,7 +552,7 @@ static bool scene_drop_poll(bContext *C,
const char **UNUSED(r_tooltip))
{
/* Ensure item under cursor is valid drop target */
- Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
return (ob && (outliner_ID_drop_find(C, event, ID_SCE) != NULL));
}
@@ -560,7 +560,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
{
Main *bmain = CTX_data_main(C);
Scene *scene = (Scene *)outliner_ID_drop_find(C, event, ID_SCE);
- Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB);
if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) {
return OPERATOR_CANCELLED;
@@ -620,7 +620,7 @@ static bool material_drop_poll(bContext *C,
const char **UNUSED(r_tooltip))
{
/* Ensure item under cursor is valid drop target */
- Material *ma = (Material *)WM_drag_ID(drag, ID_MA);
+ Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA);
return (ma && (outliner_ID_drop_find(C, event, ID_OB) != NULL));
}
@@ -628,7 +628,7 @@ static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
{
Main *bmain = CTX_data_main(C);
Object *ob = (Object *)outliner_ID_drop_find(C, event, ID_OB);
- Material *ma = (Material *)WM_drag_ID_from_event(event, ID_MA);
+ Material *ma = (Material *)WM_drag_get_local_ID_from_event(event, ID_MA);
if (ELEM(NULL, ob, ma)) {
return OPERATOR_CANCELLED;
@@ -1461,14 +1461,14 @@ static int outliner_item_drag_drop_invoke(bContext *C,
parent = scene->master_collection;
}
- WM_drag_add_ID(drag, id, &parent->id);
+ WM_drag_add_local_ID(drag, id, &parent->id);
}
BLI_freelistN(&selected.selected_array);
}
else {
/* Add single ID. */
- WM_drag_add_ID(drag, data.drag_id, data.drag_parent);
+ WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent);
}
ED_outliner_select_sync_from_outliner(C, space_outliner);
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 0294b4836c8..339cc3068d0 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -42,6 +42,7 @@ struct TreeElement;
struct TreeStoreElem;
struct ViewLayer;
struct bContext;
+struct bContextDataResult;
struct bPoseChannel;
struct wmKeyConfig;
struct wmOperatorType;
@@ -561,6 +562,12 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner
void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *space_outliner);
+/* outliner_context.c ------------------------------------------- */
+
+int outliner_context(const struct bContext *C,
+ const char *member,
+ struct bContextDataResult *result);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 159a4616ba7..492fc5c23bc 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -737,7 +737,7 @@ static void id_local_fn(bContext *C,
}
static void object_proxy_to_override_convert_fn(bContext *C,
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *UNUSED(scene),
TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
@@ -754,8 +754,15 @@ static void object_proxy_to_override_convert_fn(bContext *C,
return;
}
- BKE_lib_override_library_proxy_convert(
- CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy);
+ if (!BKE_lib_override_library_proxy_convert(
+ CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) {
+ BKE_reportf(
+ reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not create a library override from proxy '%s' (might use already local data?)",
+ ob_proxy->id.name + 2);
+ return;
+ }
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_WINDOW, NULL);
@@ -1685,6 +1692,8 @@ 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,
@@ -1710,6 +1719,8 @@ 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", ""},
@@ -1915,6 +1926,14 @@ 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(
@@ -2791,7 +2810,7 @@ static int do_outliner_operation_event(bContext *C,
}
if (datalevel == TSE_ID_BASE) {
/* do nothing... there are no ops needed here yet */
- return 0;
+ return OPERATOR_CANCELLED;
}
if (datalevel == TSE_CONSTRAINT) {
return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
@@ -2802,7 +2821,7 @@ static int do_outliner_operation_event(bContext *C,
return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
}
- return 0;
+ return OPERATOR_CANCELLED;
}
static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 7308b161d18..56eedcd3748 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -908,9 +908,9 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
/**
* TODO: this function needs to be split up! It's getting a bit too large...
*
- * \note: "ID" is not always a real ID
- * \note: If child items are only added to the tree if the item is open, the TSE_ type _must_ be
- * added to #outliner_element_needs_rebuild_on_open_change().
+ * \note "ID" is not always a real ID.
+ * \note If child items are only added to the tree if the item is open,
+ * the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change().
*/
TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
ListBase *lb,
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 3d675fdd9e4..c7c207caca0 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -451,6 +451,7 @@ void ED_spacetype_outliner(void)
st->dropboxes = outliner_dropboxes;
st->id_remap = outliner_id_remap;
st->deactivate = outliner_deactivate;
+ st->context = outliner_context;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
index bd0870c837c..cb5f42f08e1 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
@@ -24,6 +24,8 @@
#include "BKE_collection.h"
#include "BKE_main.h"
+#include "DNA_collection_types.h"
+
#include "BLT_translation.h"
#include "../outliner_intern.h"
diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
index c88eb957dd1..f7740f4648f 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
@@ -20,6 +20,7 @@
#include <iostream>
+#include "DNA_collection_types.h"
#include "DNA_scene_types.h"
#include "BKE_layer.h"
diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.hh b/source/blender/editors/space_outliner/tree/tree_element_nla.hh
index 3ca62b13bd8..c94287ce576 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_nla.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_nla.hh
@@ -23,7 +23,6 @@
#include "tree_element.hh"
struct NlaTrack;
-struct NlaStrip;
namespace blender::ed::outliner {
diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c
index bde7bdb77f1..50cfa2e71c7 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -152,7 +152,7 @@ void SCRIPT_OT_reload(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reload Scripts";
- ot->description = "Reload Scripts";
+ ot->description = "Reload scripts";
ot->idname = "SCRIPT_OT_reload";
/* api callbacks */
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 37dfcdbc765..71433a6978a 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -81,9 +81,18 @@ typedef struct SequencerAddData {
#define SEQPROP_ENDFRAME (1 << 1)
#define SEQPROP_NOPATHS (1 << 2)
#define SEQPROP_NOCHAN (1 << 3)
+#define SEQPROP_FIT_METHOD (1 << 4)
#define SELECT 1
+static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
+ {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"},
+ {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Keep image at its original size"},
+ {0, NULL, 0, NULL, NULL},
+};
+
static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
{
PropertyRNA *prop;
@@ -123,6 +132,15 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
prop = RNA_def_boolean(
ot->srna, "overlap", 0, "Allow Overlap", "Don't correct overlap on new sequence strips");
RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ if (flag & SEQPROP_FIT_METHOD) {
+ ot->prop = RNA_def_enum(ot->srna,
+ "fit_method",
+ scale_fit_methods,
+ SEQ_SCALE_TO_FIT,
+ "Fit Method",
+ "Scale fit method");
+ }
}
static void sequencer_generic_invoke_path__internal(bContext *C,
@@ -206,6 +224,8 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato
seq_load->end_frame = seq_load->start_frame;
seq_load->channel = RNA_int_get(op->ptr, "channel");
seq_load->len = 1;
+ seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method");
+ SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method);
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
/* Full path, file is set by the caller. */
@@ -659,6 +679,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
if (ed && ed->seqbasep && ed->seqbasep->first) {
RNA_boolean_set(op->ptr, "use_framerate", false);
}
+ RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* This is for drag and drop. */
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
@@ -725,7 +746,7 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME);
+ sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie");
RNA_def_boolean(ot->srna,
"use_framerate",
@@ -928,6 +949,9 @@ static int sequencer_add_image_strip_invoke(bContext *C,
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
+ const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method);
+
/* Name set already by drag and drop. */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
sequencer_generic_invoke_xy__internal(
@@ -972,7 +996,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME);
+ sequencer_generic_props__internal(ot,
+ SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna,
"use_placeholders",
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 081714991ff..d7d601a3c76 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -227,16 +227,16 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
* \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
* \param stepsize: The width of a pixel.
*/
-static void draw_seq_waveform(View2D *v2d,
- const bContext *C,
- SpaceSeq *sseq,
- Scene *scene,
- Sequence *seq,
- float x1,
- float y1,
- float x2,
- float y2,
- float stepsize)
+static void draw_seq_waveform_overlay(View2D *v2d,
+ const bContext *C,
+ SpaceSeq *sseq,
+ Scene *scene,
+ Sequence *seq,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float stepsize)
{
/* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
int x1_offset = max_ff(v2d->cur.xmin, x1);
@@ -603,121 +603,110 @@ static void draw_seq_outline(Sequence *seq,
}
}
-/* Draw info text on a sequence strip. */
-static void draw_seq_text(View2D *v2d,
- Sequence *seq,
- SpaceSeq *sseq,
- float x1,
- float x2,
- float y1,
- float y2,
- bool seq_active,
- bool y_threshold)
+static const char *draw_seq_text_get_name(Sequence *seq)
{
- rctf rect;
- char str[32 + FILE_MAX];
- size_t str_len;
const char *name = seq->name + 2;
- uchar col[4];
-
- /* All strings should include name. */
if (name[0] == '\0') {
name = BKE_sequence_give_name(seq);
}
+ return name;
+}
- if (ELEM(seq->type, SEQ_TYPE_META, SEQ_TYPE_ADJUSTMENT)) {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t source_len)
+{
+ /* Set source for the most common types. */
+ if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) {
+ BLI_snprintf(r_source, source_len, "%s%s", seq->strip->dir, seq->strip->stripdata->name);
}
- else if (seq->type == SEQ_TYPE_SCENE) {
- if (seq->scene) {
- if (seq->scene_camera) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s (%s) | %d",
- name,
- seq->scene->id.name + 2,
- ((ID *)seq->scene_camera)->name + 2,
- seq->len);
- }
- else {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->scene->id.name + 2, seq->len);
- }
- }
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ else if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ if (seq->sound) {
+ BLI_snprintf(r_source, source_len, "%s", seq->sound->filepath);
}
}
- else if (seq->type == SEQ_TYPE_MOVIECLIP) {
- if (seq->clip && !STREQ(name, seq->clip->id.name + 2)) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->clip->id.name + 2, seq->len);
- }
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
- }
+ else if (seq->type == SEQ_TYPE_MULTICAM) {
+ BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source);
}
- else if (seq->type == SEQ_TYPE_MASK) {
- if (seq->mask && !STREQ(name, seq->mask->id.name + 2)) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->mask->id.name + 2, seq->len);
+ else if (seq->type == SEQ_TYPE_TEXT) {
+ TextVars *textdata = seq->effectdata;
+ BLI_snprintf(r_source, source_len, "%s", textdata->text);
+ }
+ else if (seq->type == SEQ_TYPE_SCENE) {
+ if (seq->scene_camera) {
+ BLI_snprintf(r_source,
+ source_len,
+ "%s (%s)",
+ seq->scene->id.name + 2,
+ ((ID *)seq->scene_camera)->name + 2);
}
else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ BLI_snprintf(r_source, source_len, "%s", seq->scene->id.name + 2);
}
}
- else if (seq->type == SEQ_TYPE_MULTICAM) {
- str_len = BLI_snprintf(str, sizeof(str), "Cam %s: %d", name, seq->multicam_source);
- }
- else if (seq->type == SEQ_TYPE_IMAGE) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s%s | %d",
- name,
- seq->strip->dir,
- seq->strip->stripdata->name,
- seq->len);
+ else if (seq->type == SEQ_TYPE_MOVIECLIP) {
+ BLI_snprintf(r_source, source_len, "%s", seq->clip->id.name + 2);
}
- else if (seq->type == SEQ_TYPE_TEXT) {
- TextVars *textdata = seq->effectdata;
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", textdata->text, seq->startdisp);
+ else if (seq->type == SEQ_TYPE_MASK) {
+ BLI_snprintf(r_source, source_len, "%s", seq->mask->id.name + 2);
}
- else if (seq->type & SEQ_TYPE_EFFECT) {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ else {
+ *r_source = '\0';
}
- else if (seq->type == SEQ_TYPE_SOUND_RAM) {
- /* If a waveform is drawn, avoid to draw text when there is not enough vertical space. */
- if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 &&
- ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
+}
- str[0] = 0;
- str_len = 0;
- }
- else if (seq->sound) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->sound->filepath, seq->len);
+static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq,
+ Sequence *seq,
+ char *r_overlay_string,
+ size_t overlay_string_len)
+{
+ const char *name = draw_seq_text_get_name(seq);
+ char source[FILE_MAX];
+ int strip_duration = seq->enddisp - seq->startdisp;
+ draw_seq_text_get_source(seq, source, sizeof(source));
+
+ bool show_name = sseq->flag & SEQ_SHOW_STRIP_NAME;
+ bool show_source = (sseq->flag & (SEQ_SHOW_STRIP_SOURCE)) && source[0] != '\0';
+ bool show_duration = sseq->flag & SEQ_SHOW_STRIP_DURATION;
+
+ size_t string_len = 0;
+ if (show_name) {
+ string_len = BLI_snprintf(r_overlay_string, overlay_string_len, "%s", name);
+ if (show_source || show_duration) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
}
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ }
+ if (show_source) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, "%s", source);
+ if (show_duration) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
}
}
- else if (seq->type == SEQ_TYPE_MOVIE) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s%s | %d",
- name,
- seq->strip->dir,
- seq->strip->stripdata->name,
- seq->len);
+ if (show_duration) {
+ string_len += BLI_snprintf(
+ r_overlay_string + string_len, overlay_string_len, "%d", strip_duration);
}
- else {
- /* Should never get here!, but might with files from future. */
- BLI_assert(0);
+ return string_len;
+}
+
+/* Draw info text on a sequence strip. */
+static void draw_seq_text_overlay(View2D *v2d,
+ Sequence *seq,
+ SpaceSeq *sseq,
+ float x1,
+ float x2,
+ float y1,
+ float y2,
+ bool seq_active)
+{
+ char overlay_string[FILE_MAX];
+ size_t overlay_string_len = draw_seq_text_get_overlay_string(
+ sseq, seq, overlay_string, sizeof(overlay_string));
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ if (overlay_string_len == 0) {
+ return;
}
/* White text for the active strip. */
+ uchar col[4];
col[0] = col[1] = col[2] = seq_active ? 255 : 10;
col[3] = 255;
@@ -731,15 +720,16 @@ static void draw_seq_text(View2D *v2d,
}
}
+ rctf rect;
rect.xmin = x1;
rect.ymin = y1;
rect.xmax = x2;
rect.ymax = y2;
- UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
+ UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col);
}
-static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, float pixely)
+static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely)
{
float x1, x2, y1, y2;
uchar col[4], blend_col[3];
@@ -988,7 +978,7 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo,
* - Volume for sound strips.
* - Opacity for the other types.
*/
-static void draw_seq_fcurve(
+static void draw_seq_fcurve_overlay(
Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx)
{
FCurve *fcu;
@@ -1085,11 +1075,21 @@ static void draw_seq_strip(const bContext *C,
x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
- /* Calculate height needed for drawing text on strip. */
- float text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely);
+ float text_margin_y;
+ bool y_threshold;
+ if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) ||
+ (sseq->flag & SEQ_SHOW_STRIP_DURATION)) {
- /* Is there enough space for drawing something else than text? */
- bool y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac;
+ /* Calculate height needed for drawing text on strip. */
+ text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely);
+
+ /* Is there enough space for drawing something else than text? */
+ y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac;
+ }
+ else {
+ text_margin_y = y2;
+ y_threshold = 1;
+ }
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1102,12 +1102,13 @@ static void draw_seq_strip(const bContext *C,
}
/* Draw strip offsets when flag is enabled or during "solo preview". */
- if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
- if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) {
- draw_sequence_extensions(scene, seq, pos, pixely);
+ if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
+ if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) {
+ draw_sequence_extensions_overlay(scene, seq, pos, pixely);
+ }
}
}
-
immUnbindProgram();
x1 = seq->startdisp;
@@ -1118,24 +1119,24 @@ static void draw_seq_strip(const bContext *C,
drawmeta_contents(scene, seq, x1, y1, x2, y2);
}
- if (sseq->flag & SEQ_SHOW_FCURVES) {
- draw_seq_fcurve(scene, v2d, seq, x1, y1, x2, y2, pixelx);
+ if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) {
+ draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx);
}
/* Draw sound strip waveform. */
- if ((seq->type == SEQ_TYPE_SOUND_RAM) && (sseq->flag & SEQ_NO_WAVEFORMS) == 0) {
- draw_seq_waveform(v2d,
- C,
- sseq,
- scene,
- seq,
- x1,
- y_threshold ? y1 + 0.05f : y1,
- x2,
- y_threshold ? text_margin_y : y2,
- BLI_rctf_size_x(&region->v2d.cur) / region->winx);
+ if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) &&
+ (sseq->flag & SEQ_NO_WAVEFORMS) == 0) {
+ draw_seq_waveform_overlay(v2d,
+ C,
+ sseq,
+ scene,
+ seq,
+ x1,
+ y_threshold ? y1 + 0.05f : y1,
+ x2,
+ y_threshold ? text_margin_y : y2,
+ BLI_rctf_size_x(&region->v2d.cur) / region->winx);
}
-
/* Draw locked state. */
if (seq->flag & SEQ_LOCK) {
draw_seq_locked(x1, y1, x2, y2);
@@ -1162,11 +1163,21 @@ static void draw_seq_strip(const bContext *C,
calculate_seq_text_offsets(v2d, seq, &x1, &x2, pixelx);
- /* Don't draw strip if there is not enough vertical or horizontal space. */
- if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) {
- /* Depending on the vertical space, draw text on top or in the center of strip. */
- draw_seq_text(
- v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active, y_threshold);
+ /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */
+ if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 &&
+ ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
+ return;
+ }
+ }
+
+ if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) {
+ /* Don't draw strip if there is not enough vertical or horizontal space. */
+ if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) {
+ /* Depending on the vertical space, draw text on top or in the center of strip. */
+ draw_seq_text_overlay(
+ v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active);
+ }
}
}
@@ -1360,7 +1371,7 @@ static void sequencer_display_size(Scene *scene, float r_viewrect[2])
r_viewrect[0] *= scene->r.xasp / scene->r.yasp;
}
-static void sequencer_draw_gpencil(const bContext *C)
+static void sequencer_draw_gpencil_overlay(const bContext *C)
{
/* Draw grease-pencil (image aligned). */
ED_annotation_draw_2dimage(C);
@@ -1373,7 +1384,9 @@ static void sequencer_draw_gpencil(const bContext *C)
}
/* Draw content and safety borders borders. */
-static void sequencer_draw_borders(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene)
+static void sequencer_draw_borders_overlay(const SpaceSeq *sseq,
+ const View2D *v2d,
+ const Scene *scene)
{
float x1 = v2d->tot.xmin;
float y1 = v2d->tot.ymin;
@@ -1825,17 +1838,17 @@ void sequencer_draw_preview(const bContext *C,
C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop);
/* Draw over image. */
- if (sseq->flag & SEQ_SHOW_METADATA) {
+ if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) {
ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0);
}
}
- if (show_imbuf) {
- sequencer_draw_borders(sseq, v2d, scene);
+ if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ sequencer_draw_borders_overlay(sseq, v2d, scene);
}
- if (draw_gpencil && show_imbuf) {
- sequencer_draw_gpencil(C);
+ if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ sequencer_draw_gpencil_overlay(C);
}
#if 0
sequencer_draw_maskedit(C, scene, region, sseq);
@@ -2292,7 +2305,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* Get timeline bound-box, needed for the scroll-bars. */
- boundbox_seq(scene, &v2d->tot);
+ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot);
draw_seq_backdrop(v2d);
UI_view2d_constant_grid_draw(v2d, FPS);
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 93b17830c0f..ddc9ba2e0f6 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -183,117 +183,13 @@ bool sequencer_view_strips_poll(bContext *C)
/** \name Remove Gaps Operator
* \{ */
-static bool sequence_offset_after_frame(Scene *scene, const int delta, const int timeline_frame)
-{
- Sequence *seq;
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- bool done = false;
- TimeMarker *marker;
-
- /* All strips >= timeline_frame are shifted. */
-
- if (ed == NULL) {
- return 0;
- }
-
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (seq->startdisp >= timeline_frame) {
- BKE_sequence_translate(scene, seq, delta);
- BKE_sequence_calc(scene, seq);
- BKE_sequence_invalidate_cache_preprocessed(scene, seq);
- done = true;
- }
- }
-
- if (!scene->toolsettings->lock_markers) {
- for (marker = scene->markers.first; marker; marker = marker->next) {
- if (marker->frame >= timeline_frame) {
- marker->frame += delta;
- }
- }
- }
-
- return done;
-}
-
-void boundbox_seq(Scene *scene, rctf *rect)
-{
- Sequence *seq;
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- float min[2], max[2];
-
- if (ed == NULL) {
- return;
- }
-
- min[0] = SFRA;
- max[0] = EFRA + 1;
- min[1] = 0.0;
- max[1] = 8.0;
-
- seq = ed->seqbasep->first;
- while (seq) {
-
- if (min[0] > seq->startdisp - 1) {
- min[0] = seq->startdisp - 1;
- }
- if (max[0] < seq->enddisp + 1) {
- max[0] = seq->enddisp + 1;
- }
- if (max[1] < seq->machine + 2) {
- max[1] = seq->machine + 2;
- }
-
- seq = seq->next;
- }
-
- rect->xmin = min[0];
- rect->xmax = max[0];
- rect->ymin = min[1];
- rect->ymax = max[1];
-}
-
static int sequencer_gap_remove_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- rctf rectf;
- int timeline_frame, efra, sfra;
- bool first = false, done;
- bool do_all = RNA_boolean_get(op->ptr, "all");
-
- /* Get first and last frame. */
- boundbox_seq(scene, &rectf);
- sfra = (int)rectf.xmin;
- efra = (int)rectf.xmax;
-
- /* Check if the current frame has a gap already. */
- for (timeline_frame = CFRA; timeline_frame >= sfra; timeline_frame--) {
- if (SEQ_render_evaluate_frame(scene, timeline_frame)) {
- first = true;
- break;
- }
- }
+ const bool do_all = RNA_boolean_get(op->ptr, "all");
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
- for (; timeline_frame < efra; timeline_frame++) {
- /* There's still no strip to remove a gap for. */
- if (first == false) {
- if (SEQ_render_evaluate_frame(scene, timeline_frame)) {
- first = true;
- }
- }
- else if (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) {
- done = true;
- while (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) {
- done = sequence_offset_after_frame(scene, -1, timeline_frame);
- if (done == false) {
- break;
- }
- }
- if (done == false || do_all == false) {
- break;
- }
- }
- }
+ SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -330,9 +226,9 @@ void SEQUENCER_OT_gap_remove(struct wmOperatorType *ot)
static int sequencer_gap_insert_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- int frames = RNA_int_get(op->ptr, "frames");
-
- sequence_offset_after_frame(scene, frames, CFRA);
+ const int frames = RNA_int_get(op->ptr, "frames");
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ SEQ_offset_after_frame(scene, ed->seqbasep, frames, CFRA);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -537,7 +433,7 @@ static int slip_add_sequences_recursive(
for (seq = seqbasep->first; seq; seq = seq->next) {
if (!do_trim || (!(seq->type & SEQ_TYPE_EFFECT) && (seq->flag & SELECT))) {
seq_array[offset + num_items] = seq;
- trim[offset + num_items] = do_trim;
+ trim[offset + num_items] = do_trim && ((seq->type & SEQ_TYPE_EFFECT) == 0);
num_items++;
if (seq->type == SEQ_TYPE_META) {
@@ -545,9 +441,6 @@ static int slip_add_sequences_recursive(
num_items += slip_add_sequences_recursive(
&seq->seqbase, seq_array, trim, num_items + offset, false);
}
- else if (seq->type & SEQ_TYPE_EFFECT) {
- trim[offset + num_items] = false;
- }
}
}
@@ -2634,7 +2527,7 @@ void ED_sequencer_deselect_all(Scene *scene)
SEQ_CURRENT_END;
}
-static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
+static int sequencer_paste_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -2643,8 +2536,25 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
int ofs;
Sequence *iseq, *iseq_first;
+ if (BLI_listbase_count(&seqbase_clipboard) == 0) {
+ BKE_report(op->reports, RPT_INFO, "No strips to paste");
+ return OPERATOR_CANCELLED;
+ }
+
ED_sequencer_deselect_all(scene);
- ofs = scene->r.cfra - seqbase_clipboard_frame;
+ if (RNA_boolean_get(op->ptr, "keep_offset")) {
+ ofs = scene->r.cfra - seqbase_clipboard_frame;
+ }
+ else {
+ int min_seq_startdisp = INT_MAX;
+ LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) {
+ if (seq->startdisp < min_seq_startdisp) {
+ min_seq_startdisp = seq->startdisp;
+ }
+ }
+ /* Paste strips after playhead. */
+ ofs = scene->r.cfra - min_seq_startdisp;
+ }
/* Copy strips, temporarily restoring pointers to actual data-blocks. This
* must happen on the clipboard itself, so that copying does user counting
@@ -2692,6 +2602,11 @@ void SEQUENCER_OT_paste(wmOperatorType *ot)
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties. */
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "keep_offset", false, "Keep Offset", "Keep strip offset to playhead when pasting");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -3499,3 +3414,148 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Strip Transform Operator
+ * \{ */
+
+enum {
+ STRIP_TRANSFORM_POSITION,
+ STRIP_TRANSFORM_SCALE,
+ STRIP_TRANSFORM_ROTATION,
+ STRIP_TRANSFORM_ALL,
+};
+
+static const EnumPropertyItem transform_reset_properties[] = {
+ {STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"},
+ {STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"},
+ {STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"},
+ {STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *seq;
+ const int property = RNA_enum_get(op->ptr, "property");
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
+ StripTransform *transform = seq->strip->transform;
+ switch (property) {
+ case STRIP_TRANSFORM_POSITION:
+ transform->xofs = 0;
+ transform->yofs = 0;
+ break;
+ case STRIP_TRANSFORM_SCALE:
+ transform->scale_x = 1.0f;
+ transform->scale_y = 1.0f;
+ break;
+ case STRIP_TRANSFORM_ROTATION:
+ transform->rotation = 0.0f;
+ break;
+ case STRIP_TRANSFORM_ALL:
+ transform->xofs = 0;
+ transform->yofs = 0;
+ transform->scale_x = 1.0f;
+ transform->scale_y = 1.0f;
+ transform->rotation = 0.0f;
+ break;
+ }
+ BKE_sequence_invalidate_cache_preprocessed(scene, seq);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Clear Strip Transform";
+ ot->idname = "SEQUENCER_OT_strip_transform_clear";
+ ot->description = "Reset image transformation to default value";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_strip_transform_clear_exec;
+ ot->poll = sequencer_edit_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna,
+ "property",
+ transform_reset_properties,
+ STRIP_TRANSFORM_ALL,
+ "Property",
+ "Strip transform property to be reset");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform Set Fit Operator
+ * \{ */
+
+static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"},
+ {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *seq;
+ const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method");
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
+ const int timeline_frame = CFRA;
+ StripElem *strip_elem = SEQ_render_give_stripelem(seq, timeline_frame);
+
+ if (strip_elem == NULL) {
+ continue;
+ }
+
+ SEQ_set_scale_to_fit(seq,
+ strip_elem->orig_width,
+ strip_elem->orig_height,
+ scene->r.xsch,
+ scene->r.ysch,
+ fit_method);
+ BKE_sequence_invalidate_cache_preprocessed(scene, seq);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Strip Transform Set Fit";
+ ot->idname = "SEQUENCER_OT_strip_transform_fit";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_strip_transform_fit_exec;
+ ot->poll = sequencer_edit_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna,
+ "fit_method",
+ scale_fit_methods,
+ SEQ_SCALE_TO_FIT,
+ "Fit Method",
+ "Scale fit fit_method");
+}
+
+/** \} */
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 1ea4fb05d53..4c942a83f2b 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -71,7 +71,6 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain,
/* sequencer_edit.c */
struct View2D;
void seq_rectf(struct Sequence *seq, struct rctf *rectf);
-void boundbox_seq(struct Scene *scene, struct rctf *rect);
struct Sequence *find_nearest_seq(struct Scene *scene,
struct View2D *v2d,
int *hand,
@@ -145,6 +144,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot);
void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot);
void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot);
+void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot);
+void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot);
/* sequencer_select.c */
void SEQUENCER_OT_select_all(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index bdf6e4ece7f..7bfc8600544 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -81,6 +81,8 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_change_path);
WM_operatortype_append(SEQUENCER_OT_set_range_to_strips);
+ WM_operatortype_append(SEQUENCER_OT_strip_transform_clear);
+ WM_operatortype_append(SEQUENCER_OT_strip_transform_fit);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 75d92d5f00d..e12c43b7804 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -87,8 +87,10 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op)
rctf box;
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
- boundbox_seq(CTX_data_scene(C), &box);
+ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box);
UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 45c7bac54f8..2bf4741e4f5 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -99,7 +99,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
sseq->view = SEQ_VIEW_SEQUENCE;
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_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME |
+ SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION;
/* Tool header. */
region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
@@ -706,7 +707,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region)
SpaceSeq *sseq = area->spacedata.first;
Scene *scene = CTX_data_scene(C);
wmWindowManager *wm = CTX_wm_manager(C);
- const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW));
+ const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) &&
+ (sseq->flag & SEQ_SHOW_STRIP_OVERLAY));
/* XXX temp fix for wrong setting in sseq->mainb */
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index bb71a9b11be..0f5ac5abe1d 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -357,7 +357,7 @@ static bool text_drop_paste_poll(bContext *UNUSED(C),
static void text_drop_paste(wmDrag *drag, wmDropBox *drop)
{
char *text;
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
text = RNA_path_full_ID_py(G_MAIN, id);
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index f8b7c62686f..932bacfb8a0 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -2588,7 +2588,7 @@ static int text_scroll_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- txt_screen_skip(st, region, lines * U.wheellinescroll);
+ txt_screen_skip(st, region, lines * 3);
ED_area_tag_redraw(CTX_wm_area(C));
diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c
index d823530fd89..ee23cde78c2 100644
--- a/source/blender/editors/space_userpref/userpref_ops.c
+++ b/source/blender/editors/space_userpref/userpref_ops.c
@@ -30,6 +30,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BKE_report.h"
#include "RNA_access.h"
@@ -133,9 +134,71 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Add Asset Library Operator
+ * \{ */
+
+static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ BKE_preferences_asset_library_add(&U, NULL, NULL);
+ U.runtime.is_dirty = true;
+ return OPERATOR_FINISHED;
+}
+
+static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot)
+{
+ ot->name = "Add Asset Library";
+ ot->idname = "PREFERENCES_OT_asset_library_add";
+ ot->description =
+ "Add a path to a .blend file to be used by the Asset Browser as source of assets";
+
+ ot->exec = preferences_asset_library_add_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Remove Asset Library Operator
+ * \{ */
+
+static int preferences_asset_library_remove_exec(bContext *UNUSED(C), wmOperator *op)
+{
+ const int index = RNA_int_get(op->ptr, "index");
+ bUserAssetLibrary *library = BLI_findlink(&U.asset_libraries, index);
+ if (library) {
+ BKE_preferences_asset_library_remove(&U, library);
+ U.runtime.is_dirty = true;
+ /* Trigger refresh for the Asset Browser. */
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot)
+{
+ ot->name = "Remove Asset Library";
+ ot->idname = "PREFERENCES_OT_asset_library_remove";
+ ot->description =
+ "Remove a path to a .blend file, so the Asset Browser will not attempt to show it anymore";
+
+ ot->exec = preferences_asset_library_remove_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
+}
+
+/** \} */
+
void ED_operatortypes_userpref(void)
{
WM_operatortype_append(PREFERENCES_OT_reset_default_theme);
+
WM_operatortype_append(PREFERENCES_OT_autoexec_path_add);
WM_operatortype_append(PREFERENCES_OT_autoexec_path_remove);
+
+ WM_operatortype_append(PREFERENCES_OT_asset_library_add);
+ WM_operatortype_append(PREFERENCES_OT_asset_library_remove);
}
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index 0371b4e271f..9242fc15021 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -22,13 +22,13 @@ set(INC
../../blenlib
../../blentranslation
../../bmesh
+ ../../depsgraph
../../draw
../../gpu
../../imbuf
../../makesdna
../../makesrna
../../render
- ../../depsgraph
../../windowmanager
../../../../intern/glew-mx
../../../../intern/guardedalloc
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 9f31e7a411d..3761f4ad7c6 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -461,6 +461,12 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region)
ED_view3d_stop_render_preview(wm, region);
}
+static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event)
+{
+ ScrArea *area = CTX_wm_area(C);
+ return ED_region_overlap_isect_any_xy(area, &event->x) == false;
+}
+
static ID *view3d_drop_id_in_main_region_poll_id(bContext *C,
wmDrag *drag,
const wmEvent *event,
@@ -470,7 +476,7 @@ static ID *view3d_drop_id_in_main_region_poll_id(bContext *C,
if (ED_region_overlap_isect_any_xy(area, &event->x)) {
return NULL;
}
- return WM_drag_ID(drag, id_type);
+ return view3d_drop_in_main_region_poll(C, event) ? WM_drag_get_local_ID(drag, id_type) : NULL;
}
static bool view3d_drop_id_in_main_region_poll(bContext *C,
@@ -478,7 +484,11 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C,
const wmEvent *event,
ID_Type id_type)
{
- return (view3d_drop_id_in_main_region_poll_id(C, drag, event, id_type) != NULL);
+ if (!view3d_drop_in_main_region_poll(C, event)) {
+ return false;
+ }
+
+ return WM_drag_get_local_ID(drag, id_type) || WM_drag_get_asset_data(drag, id_type);
}
static bool view3d_ob_drop_poll(bContext *C,
@@ -533,7 +543,7 @@ static bool view3d_ima_drop_poll(bContext *C,
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- return WM_drag_ID(drag, ID_IM) != NULL;
+ return WM_drag_get_local_ID(drag, ID_IM) || WM_drag_get_asset_data(drag, ID_IM);
}
static bool view3d_ima_bg_is_camera_view(bContext *C)
@@ -596,14 +606,14 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C),
static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, ID_OB);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, ID_GR);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_GR);
drop->opcontext = WM_OP_EXEC_DEFAULT;
RNA_string_set(drop->ptr, "name", id->name + 2);
@@ -611,14 +621,14 @@ static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
RNA_enum_set(drop->ptr, "type", GS(id->name));
@@ -626,7 +636,7 @@ static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop)
static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
if (id) {
RNA_string_set(drop->ptr, "name", id->name + 2);
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 5b400bbf60a..96bd25f85e7 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2457,13 +2457,16 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
else if (obact && BKE_paint_select_face_test(obact)) {
retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
if (!retval && deselect_all) {
- retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
+ retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, true);
}
}
else if (BKE_paint_select_vert_test(obact)) {
retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
if (!retval && deselect_all) {
retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
+ if (retval) {
+ paintvert_tag_select_update(C, obact);
+ }
}
}
else {
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 1be9bd27c7a..2b7b8255068 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -1620,6 +1620,41 @@ void ED_view3d_to_object(const Depsgraph *depsgraph,
BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true);
}
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob)
+{
+ Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
+ float co[3]; /* the new location to apply */
+ float scale; /* only for ortho cameras */
+
+ if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) {
+ ObjectTfmProtectedChannels obtfm;
+ float obmat_new[4][4];
+
+ if ((camera_ob_eval->type == OB_CAMERA) &&
+ (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
+ ((Camera *)camera_ob->data)->ortho_scale = scale;
+ }
+
+ copy_m4_m4(obmat_new, camera_ob_eval->obmat);
+ copy_v3_v3(obmat_new[3], co);
+
+ /* only touch location */
+ BKE_object_tfm_protected_backup(camera_ob, &obtfm);
+ BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
+ BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
+
+ /* notifiers */
+ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM);
+
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index a24f59019f0..9d947384bf0 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -535,40 +535,18 @@ void VIEW3D_OT_camera_to_view(wmOperatorType *ot)
* meant to take into account vertex/bone selection for eg. */
static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
Object *camera_ob = v3d ? v3d->camera : scene->camera;
- Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
-
- float r_co[3]; /* the new location to apply */
- float r_scale; /* only for ortho cameras */
- if (camera_ob_eval == NULL) {
+ if (camera_ob == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active camera");
return OPERATOR_CANCELLED;
}
- /* this function does all the important stuff */
- if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, r_co, &r_scale)) {
- ObjectTfmProtectedChannels obtfm;
- float obmat_new[4][4];
-
- if ((camera_ob_eval->type == OB_CAMERA) &&
- (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
- ((Camera *)camera_ob->data)->ortho_scale = r_scale;
- }
-
- copy_m4_m4(obmat_new, camera_ob_eval->obmat);
- copy_v3_v3(obmat_new[3], r_co);
-
- /* only touch location */
- BKE_object_tfm_protected_backup(camera_ob, &obtfm);
- BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
- BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
-
- /* notifiers */
- DEG_id_tag_update(&camera_ob->id, ID_RECALC_TRANSFORM);
+ if (ED_view3d_camera_to_view_selected(bmain, depsgraph, scene, camera_ob)) {
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 73d6a376da6..a2eb68a1b71 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -22,12 +22,12 @@ set(INC
../../blenlib
../../blentranslation
../../bmesh
+ ../../depsgraph
../../gpu
../../ikplugin
../../makesdna
../../makesrna
../../render
- ../../depsgraph
../../sequencer
../../windowmanager
../../../../intern/glew-mx
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 227330e8524..91bf2bf8aac 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -52,12 +52,12 @@ struct SnapObjectContext;
struct TransDataContainer;
struct TransInfo;
struct TransSnap;
-struct TransformOrientation;
struct ViewLayer;
struct bContext;
struct wmEvent;
struct wmKeyConfig;
struct wmKeyMap;
+struct wmOperator;
struct wmTimer;
/** #TransInfo.redraw */
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index b753572ea7b..59fcd016020 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -29,12 +29,9 @@ struct FCurve;
struct ListBase;
struct Object;
struct TransData;
-struct TransDataContainer;
struct TransDataCurveHandleFlags;
struct TransInfo;
struct bContext;
-struct bKinematicConstraint;
-struct bPoseChannel;
/* transform_convert.c */
void transform_autoik_update(TransInfo *t, short mode);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 90c1f241338..8597c372537 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -377,6 +377,9 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
else {
mul_v3_m3v3(global_dir, t->spacemtx, global_dir);
}
+ if (t->flag & T_2D_EDIT) {
+ removeAspectRatio(t, global_dir);
+ }
}
else {
copy_v3_v3(global_dir, t->values);
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 5153fdedcae..5d758a6c6c6 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -717,7 +717,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
"use_automerge_and_split",
0,
"Auto Merge & Split",
- "Forces the use of Auto Merge & Split");
+ "Forces the use of Auto Merge and Split");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
}
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index 5bee572c603..db8ec943bfd 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -25,8 +25,6 @@
/* For enum. */
#include "DNA_space_types.h"
-struct SnapObjectParams;
-
bool peelObjectsTransform(struct TransInfo *t,
const float mval[2],
const bool use_peel_object,
diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c
index 49417a54472..4fded419b5b 100644
--- a/source/blender/editors/undo/memfile_undo.c
+++ b/source/blender/editors/undo/memfile_undo.c
@@ -27,6 +27,7 @@
#include "BLI_listbase.h"
#include "DNA_ID.h"
+#include "DNA_collection_types.h"
#include "DNA_node_types.h"
#include "DNA_object_enums.h"
#include "DNA_object_types.h"
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index c88169778f7..bbaf5d5475a 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SRC
../include/BIF_glutil.h
../include/ED_anim_api.h
../include/ED_armature.h
+ ../include/ED_asset.h
../include/ED_buttons.h
../include/ED_clip.h
../include/ED_curve.h
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 76c261c9cba..d78758dcc1c 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -35,6 +35,7 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
@@ -44,6 +45,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_icons.h"
#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -51,6 +53,7 @@
#include "BKE_object.h"
#include "BKE_packedFile.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_undo_system.h"
#include "BKE_workspace.h"
@@ -64,6 +67,7 @@
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_paint.h"
+#include "ED_render.h"
#include "ED_space_api.h"
#include "ED_util.h"
@@ -501,3 +505,104 @@ void ED_OT_flush_edits(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_INTERNAL;
}
+
+static bool lib_id_preview_editing_poll(bContext *C)
+{
+ const PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type));
+
+ const ID *id = idptr.data;
+ if (!id) {
+ return false;
+ }
+ if (ID_IS_LINKED(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data"));
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data"));
+ return false;
+ }
+ if (!BKE_previewimg_id_get_p(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews"));
+ return false;
+ }
+
+ return true;
+}
+
+static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
+{
+ char path[FILE_MAX];
+
+ RNA_string_get(op->ptr, "filepath", path);
+
+ if (!BLI_is_file(path)) {
+ BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path);
+ return OPERATOR_CANCELLED;
+ }
+
+ PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ ID *id = idptr.data;
+
+ BKE_previewimg_id_custom_set(id, path);
+
+ WM_event_add_notifier(C, NC_ASSET, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
+{
+ ot->name = "Load Custom Preview";
+ ot->description = "Choose an image to help identify the data-block visually";
+ ot->idname = "ED_OT_lib_id_load_custom_preview";
+
+ /* api callbacks */
+ ot->poll = lib_id_preview_editing_poll;
+ ot->exec = lib_id_load_custom_preview_exec;
+ ot->invoke = WM_operator_filesel;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
+ FILE_SPECIAL,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_DEFAULT);
+}
+
+static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ ID *id = idptr.data;
+
+ ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+
+ PreviewImage *preview = BKE_previewimg_id_get(id);
+ if (preview) {
+ BKE_previewimg_clear(preview);
+ }
+ UI_icon_render_id(C, NULL, id, true, true);
+
+ WM_event_add_notifier(C, NC_ASSET, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
+{
+ ot->name = "Generate Preview";
+ ot->description = "Create an automatic preview for the selected data-block";
+ ot->idname = "ED_OT_lib_id_generate_preview";
+
+ /* api callbacks */
+ ot->poll = lib_id_preview_editing_poll;
+ ot->exec = lib_id_generate_preview_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+}
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index 8e4a3df920e..1c8a56e0608 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -35,11 +35,11 @@ set(INC
set(SRC
uvedit_buttons.c
uvedit_draw.c
+ uvedit_islands.c
uvedit_ops.c
uvedit_parametrizer.c
uvedit_path.c
uvedit_rip.c
- uvedit_islands.c
uvedit_select.c
uvedit_smart_stitch.c
uvedit_unwrap_ops.c
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 306f8a2c561..28567234fab 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -25,7 +25,6 @@
struct BMFace;
struct BMLoop;
-struct Image;
struct Object;
struct Scene;
struct SpaceImage;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 5b3d858329a..aac5b96f737 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -1812,7 +1812,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Location",
- "Cursor location in normalized (0.0-1.0) coordinates",
+ "Cursor location in normalized (0.0 to 1.0) coordinates",
-10.0f,
10.0f);
}
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 6dac31794b4..c1d222c9368 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -2808,7 +2808,7 @@ void UV_OT_stitch(wmOperatorType *ot)
RNA_def_boolean(ot->srna,
"midpoint_snap",
0,
- "Snap At Midpoint",
+ "Snap at Midpoint",
"UVs are stitched at midpoint instead of at static island");
RNA_def_boolean(ot->srna, "clear_seams", 1, "Clear Seams", "Clear seams of stitched edges");
RNA_def_enum(ot->srna,
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 37cde3162a0..d3ba5bf8b37 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -28,6 +28,7 @@
#include "BKE_object.h"
#include "BKE_screen.h"
+#include "DNA_material_types.h"
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 077a454db73..f5bfe66562b 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -33,6 +33,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h
index e5a6d9e6a8f..30e54f44499 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h
@@ -23,9 +23,7 @@
#pragma once
-struct GHash;
struct MDeformVert;
-struct Main;
struct Material;
struct Object;
struct bGPDlayer;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index aa21bf192c4..b8fa88327fc 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -237,9 +237,17 @@ static void generate_geometry(GpencilModifierData *md,
/* To ensure a nice distribution, we use halton sequence and offset using the seed. */
BLI_halton_3d(primes, offset, x, r);
- for (int i = 0; i < 3; i++) {
- rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f);
- rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f);
+ if ((mmd->flag & GP_ARRAY_UNIFORM_RANDOM_SCALE) && j == 2) {
+ float rand_value;
+ rand_value = fmodf(r[0] * 2.0 - 1.0 + rand_offset, 1.0f);
+ rand_value = fmodf(sin(rand_value * 12.9898 + j * 78.233) * 43758.5453, 1.0f);
+ copy_v3_fl(rand[j], rand_value);
+ }
+ else {
+ for (int i = 0; i < 3; i++) {
+ rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f);
+ rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f);
+ }
}
}
/* Calculate Random matrix. */
@@ -425,6 +433,7 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE);
uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE);
uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE);
+ uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
index 4e569099461..04405fed7d9 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
@@ -34,6 +34,7 @@
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index 2ee148837cd..311d08238b9 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -33,6 +33,7 @@
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 27a7ea1e6a5..55716b584c3 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -27,8 +27,6 @@
extern "C" {
#endif
-struct GPUTexture;
-struct GPUUniformBuf;
struct GPUVertBuf;
/** Opaque type hidding blender::gpu::Shader */
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index e9c081abd22..91119bd05a1 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -28,14 +28,6 @@
#include "GPU_state.h"
struct GPUVertBuf;
-struct ImBuf;
-struct Image;
-struct ImageUser;
-struct MovieClip;
-struct MovieClipUser;
-struct PreviewImage;
-
-struct GPUFrameBuffer;
/** Opaque type hiding blender::gpu::Texture. */
typedef struct GPUTexture GPUTexture;
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index 5d130d75d2c..f3d58a55814 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -31,10 +31,7 @@ extern "C" {
struct GPUMaterial;
struct GPUNodeGraph;
-struct GPUOutput;
struct GPUShader;
-struct GSet;
-struct ListBase;
typedef struct GPUPass {
struct GPUPass *next;
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index 0ef95d94c0d..929191cff73 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -33,8 +33,6 @@
struct GPUNode;
struct GPUOutput;
-struct GPUShader;
-struct GPUVertAttrLayers;
struct ListBase;
typedef enum eGPUDataSource {
diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
index 00d10776864..18ea6d18caf 100644
--- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
@@ -24,6 +24,8 @@
#include "BLI_sys_types.h"
+struct GPUUniformBuf;
+
namespace blender {
namespace gpu {
diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c
index 188c8786665..a83654d31e7 100644
--- a/source/blender/gpu/intern/gpu_viewport.c
+++ b/source/blender/gpu/intern/gpu_viewport.c
@@ -616,8 +616,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo
GPU_framebuffer_ensure_config(&dfbl->stereo_comp_fb,
{
GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(dtxl->color),
GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay),
+ GPU_ATTACHMENT_TEXTURE(dtxl->color),
});
GPUVertFormat *vert_format = immVertexFormat();
@@ -628,8 +628,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo
GPU_matrix_identity_set();
GPU_matrix_identity_projection_set();
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE);
- immUniform1i("imageTexture", 0);
- immUniform1i("overlayTexture", 1);
+ immUniform1i("overlayTexture", 0);
+ immUniform1i("imageTexture", 1);
int settings = stereo_format->display_mode;
if (settings == S3D_DISPLAY_ANAGLYPH) {
switch (stereo_format->anaglyph_type) {
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 957352595ed..58ddc918f61 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -382,6 +382,8 @@ bool IMB_anim_can_produce_frames(const struct anim *anim);
int ismovie(const char *filepath);
void IMB_anim_set_preseek(struct anim *anim, int preseek);
int IMB_anim_get_preseek(struct anim *anim);
+int IMB_anim_get_image_width(struct anim *anim);
+int IMB_anim_get_image_height(struct anim *anim);
/**
*
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index d825b20f5f2..9b01ea0840f 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -1506,3 +1506,13 @@ int IMB_anim_get_preseek(struct anim *anim)
{
return anim->preseek;
}
+
+int IMB_anim_get_image_width(struct anim *anim)
+{
+ return anim->x;
+}
+
+int IMB_anim_get_image_height(struct anim *anim)
+{
+ return anim->y;
+}
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 93cdbbb1407..7d4797def8f 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -33,6 +33,8 @@
#include "BKE_idprop.h"
+#include "DNA_ID.h" /* ID property definitions. */
+
#include "IMB_filetype.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c
index 28f09c38a96..d8abd3411cb 100644
--- a/source/blender/imbuf/intern/metadata.c
+++ b/source/blender/imbuf/intern/metadata.c
@@ -29,6 +29,8 @@
#include "BKE_idprop.h"
+#include "DNA_ID.h" /* ID property definitions. */
+
#include "MEM_guardedalloc.h"
#include "IMB_imbuf.h"
diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c
index 60fc2ac0867..561a833803d 100644
--- a/source/blender/imbuf/intern/png.c
+++ b/source/blender/imbuf/intern/png.c
@@ -32,6 +32,8 @@
#include "BKE_global.h"
#include "BKE_idprop.h"
+#include "DNA_ID.h" /* ID property definitions. */
+
#include "MEM_guardedalloc.h"
#include "IMB_imbuf.h"
diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c
index d5ded02be62..486db07597f 100644
--- a/source/blender/imbuf/intern/thumbs_blend.c
+++ b/source/blender/imbuf/intern/thumbs_blend.c
@@ -78,15 +78,7 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const
if (STREQ(blockname, blen_id)) {
if (img) {
- unsigned int w = img->w[ICON_SIZE_PREVIEW];
- unsigned int h = img->h[ICON_SIZE_PREVIEW];
- unsigned int *rect = img->rect[ICON_SIZE_PREVIEW];
-
- if (w > 0 && h > 0 && rect) {
- /* first allocate imbuf for copying preview into it */
- ima = IMB_allocImBuf(w, h, 32, IB_rect);
- memcpy(ima->rect, rect, w * h * sizeof(unsigned int));
- }
+ ima = BKE_previewimg_to_imbuf(img, ICON_SIZE_PREVIEW);
}
break;
}
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
index a0d9257b822..2098b8b2505 100644
--- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
@@ -29,7 +29,6 @@
#include <Alembic/Abc/OObject.h>
struct Depsgraph;
-struct ID;
struct Object;
namespace blender::io::alembic {
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index d23e69cf73e..4997e498199 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -29,7 +29,6 @@
#include "DNA_material_types.h"
struct IDProperty;
-struct Material;
struct Object;
namespace blender::io::alembic {
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index f8d610a3659..80f2cadd08c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -30,6 +30,7 @@
#include "BLI_math_geom.h"
+#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_particle.h"
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h
index 3759ffa4310..901fd70601f 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.h
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.h
@@ -23,9 +23,6 @@
#include <Alembic/AbcGeom/OCurves.h>
#include <vector>
-struct ParticleSettings;
-struct ParticleSystem;
-
namespace blender::io::alembic {
class ABCHairWriter : public ABCAbstractWriter {
diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h
index 2a4fd6bd8fb..67000194aa1 100644
--- a/source/blender/io/alembic/intern/abc_reader_archive.h
+++ b/source/blender/io/alembic/intern/abc_reader_archive.h
@@ -28,7 +28,6 @@
#include <fstream>
struct Main;
-struct Scene;
namespace blender::io::alembic {
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index c9eefbf9039..ad5a258f80c 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -35,6 +35,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index 77ccdeae28d..9f54bf2aa28 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -385,7 +385,10 @@ virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act)
char joint_path[100];
char rna_path[100];
- BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name);
+ char grp_name_esc[sizeof(grp->name) * 2];
+ BLI_str_escape(grp_name_esc, grp->name, sizeof(grp_name_esc));
+
+ BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp_name_esc);
BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path);
FCurve *quatcu[4] = {
diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp
index 9533ca322f9..7eef5c3f5fb 100644
--- a/source/blender/io/collada/ArmatureImporter.cpp
+++ b/source/blender/io/collada/ArmatureImporter.cpp
@@ -1037,7 +1037,9 @@ void ArmatureImporter::get_rna_path_for_joint(COLLADAFW::Node *node,
char *joint_path,
size_t count)
{
- BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bc_get_joint_name(node));
+ char bone_name_esc[sizeof(((Bone *)nullptr)->name) * 2];
+ BLI_str_escape(bone_name_esc, bc_get_joint_name(node), sizeof(bone_name_esc));
+ BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bone_name_esc);
}
/* gives a world-space mat */
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index ad1cc1035fb..bba50064879 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -832,7 +832,9 @@ void bc_enable_fcurves(bAction *act, char *bone_name)
char prefix[200];
if (bone_name) {
- BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name);
+ char bone_name_esc[sizeof(((Bone *)nullptr)->name) * 2];
+ BLI_str_escape(bone_name_esc, bone_name, sizeof(bone_name_esc));
+ BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name_esc);
}
for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) {
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index e066ca8ba8f..300c555ac8f 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -43,13 +43,11 @@
#include <set>
#include <string>
-struct Base;
struct Depsgraph;
struct DupliObject;
struct ID;
struct Object;
struct ParticleSystem;
-struct ViewLayer;
namespace blender::io {
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
index 8acdba416d3..457d68953ff 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc
@@ -36,7 +36,7 @@ namespace {
/* Mapping from ID.name to set of export hierarchy path. Duplicated objects can be exported
* multiple times with different export paths, hence the set. */
-typedef std::map<std::string, std::set<std::string>> used_writers;
+using used_writers = std::map<std::string, std::set<std::string>>;
class TestHierarchyWriter : public AbstractHierarchyWriter {
public:
diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h
index 41ff34b327e..66e87f0eed1 100644
--- a/source/blender/io/usd/intern/usd_exporter_context.h
+++ b/source/blender/io/usd/intern/usd_exporter_context.h
@@ -24,7 +24,6 @@
#include <pxr/usd/usd/common.h>
struct Depsgraph;
-struct Object;
namespace blender::io::usd {
diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
index 922ab761bd9..cd19becbb07 100644
--- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h
+++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h
@@ -28,7 +28,6 @@
#include <pxr/usd/usd/timeCode.h>
struct Depsgraph;
-struct ID;
struct Object;
namespace blender::io::usd {
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 6a3b8d515dc..2143164e3dd 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -33,7 +33,6 @@
#include "DNA_material_types.h"
struct Material;
-struct Object;
namespace blender::io::usd {
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index b9ea90736ff..40e2d0d8674 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -25,7 +25,6 @@
extern "C" {
#endif
-struct Scene;
struct bContext;
struct USDExportParams {
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index f2d860a2851..265baa16cc5 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -264,7 +264,12 @@ typedef struct IDOverrideLibrary {
typedef struct ID {
void *next, *prev;
struct ID *newid;
+
struct Library *lib;
+
+ /** If the ID is an asset, this pointer is set. Owning pointer. */
+ struct AssetMetaData *asset_data;
+
/** MAX_ID_NAME. */
char name[66];
/**
@@ -310,6 +315,7 @@ typedef struct ID {
struct ID *orig_id;
void *py_instance;
+ void *_pad1;
} ID;
/**
@@ -354,6 +360,7 @@ enum eIconSizes {
enum ePreviewImage_Flag {
PRV_CHANGED = (1 << 0),
PRV_USER_EDITED = (1 << 1), /* if user-edited, do not auto-update this anymore! */
+ PRV_UNFINISHED = (1 << 2), /* The preview is not done rendering yet. */
};
/* for PreviewImage->tag */
@@ -502,6 +509,8 @@ typedef enum ID_Type {
#define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \
(((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL)
+#define ID_IS_ASSET(_id) (((const ID *)(_id))->asset_data != NULL)
+
/* Check whether datablock type is covered by copy-on-write. */
#define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_PAL, ID_IM))
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 17d41985f80..1eafa655195 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -612,9 +612,18 @@ typedef struct FCurve {
char _pad[3];
/* RNA - data link */
- /** If applicable, the index of the RNA-array item to get. */
+ /**
+ * When the RNA property from `rna_path` is an array, use this to access the array index.
+ *
+ * \note This may be negative (as it wasn't prevented in 2.91 and older).
+ * Currently it silently fails to resolve the data-path in this case.
+ */
int array_index;
- /** RNA-path to resolve data-access. */
+ /**
+ * RNA-path to resolve data-access, see: #RNA_path_resolve_property.
+ *
+ * \note String look-ups for collection and custom-properties are escaped using #BLI_str_escape.
+ */
char *rna_path;
/* curve coloring (for editor) */
diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h
new file mode 100644
index 00000000000..ff00ba79cf0
--- /dev/null
+++ b/source/blender/makesdna/DNA_asset_defaults.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 DNA
+ */
+
+#pragma once
+
+/* Struct members on own line. */
+/* clang-format off */
+
+/* -------------------------------------------------------------------- */
+/** \name Asset Struct
+ * \{ */
+
+#define _DNA_DEFAULT_AssetMetaData \
+ { \
+ 0 \
+ }
+
+/** \} */
+
+/* clang-format on */
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
new file mode 100644
index 00000000000..671012e54ef
--- /dev/null
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_ASSET_TYPES_H__
+#define __DNA_ASSET_TYPES_H__
+
+#include "DNA_listBase.h"
+
+/**
+ * \brief User defined tag.
+ * Currently only used by assets, could be used more often at some point.
+ * Maybe add a custom icon and color to these in future?
+ */
+typedef struct AssetTag {
+ struct AssetTag *next, *prev;
+ char name[64]; /* MAX_NAME */
+} AssetTag;
+
+/**
+ * \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.
+ *
+ * \note This struct must be readable without having to read anything but blocks from the ID it is
+ * attached to! That way, asset information of a file can be read, without reading anything
+ * more than that from the file. So pointers to other IDs or ID data are strictly forbidden.
+ */
+typedef struct AssetMetaData {
+ /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */
+ struct IDProperty *properties;
+
+ /** Optional description of this asset for display in the UI. Dynamic length. */
+ char *description;
+ /** User defined tags for this asset. The asset manager uses these for filtering, but how they
+ * function exactly (e.g. how they are registered to provide a list of searchable available tags)
+ * is up to the asset-engine. */
+ ListBase tags; /* AssetTag */
+ short active_tag;
+ /** Store the number of tags to avoid continuous counting. Could be turned into runtime data, we
+ * can always reliably reconstruct it from the list. */
+ short tot_tags;
+
+ char _pad[4];
+} AssetMetaData;
+
+#endif /* __DNA_ASSET_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
new file mode 100644
index 00000000000..f12934c9104
--- /dev/null
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -0,0 +1,614 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* BrushGpencilSettings->preset_type.
+ * Use a range for each group and not continuous values.*/
+typedef enum eGPBrush_Presets {
+ GP_BRUSH_PRESET_UNKNOWN = 0,
+
+ /* Draw 1-99. */
+ GP_BRUSH_PRESET_AIRBRUSH = 1,
+ GP_BRUSH_PRESET_INK_PEN = 2,
+ GP_BRUSH_PRESET_INK_PEN_ROUGH = 3,
+ GP_BRUSH_PRESET_MARKER_BOLD = 4,
+ GP_BRUSH_PRESET_MARKER_CHISEL = 5,
+ GP_BRUSH_PRESET_PEN = 6,
+ GP_BRUSH_PRESET_PENCIL_SOFT = 7,
+ GP_BRUSH_PRESET_PENCIL = 8,
+ GP_BRUSH_PRESET_FILL_AREA = 9,
+ GP_BRUSH_PRESET_ERASER_SOFT = 10,
+ GP_BRUSH_PRESET_ERASER_HARD = 11,
+ GP_BRUSH_PRESET_ERASER_POINT = 12,
+ GP_BRUSH_PRESET_ERASER_STROKE = 13,
+ GP_BRUSH_PRESET_TINT = 14,
+
+ /* Vertex Paint 100-199. */
+ GP_BRUSH_PRESET_VERTEX_DRAW = 100,
+ GP_BRUSH_PRESET_VERTEX_BLUR = 101,
+ GP_BRUSH_PRESET_VERTEX_AVERAGE = 102,
+ GP_BRUSH_PRESET_VERTEX_SMEAR = 103,
+ GP_BRUSH_PRESET_VERTEX_REPLACE = 104,
+
+ /* Sculpt 200-299. */
+ GP_BRUSH_PRESET_SMOOTH_STROKE = 200,
+ GP_BRUSH_PRESET_STRENGTH_STROKE = 201,
+ GP_BRUSH_PRESET_THICKNESS_STROKE = 202,
+ GP_BRUSH_PRESET_GRAB_STROKE = 203,
+ GP_BRUSH_PRESET_PUSH_STROKE = 204,
+ GP_BRUSH_PRESET_TWIST_STROKE = 205,
+ GP_BRUSH_PRESET_PINCH_STROKE = 206,
+ GP_BRUSH_PRESET_RANDOMIZE_STROKE = 207,
+ GP_BRUSH_PRESET_CLONE_STROKE = 208,
+
+ /* Weight Paint 300-399. */
+ GP_BRUSH_PRESET_DRAW_WEIGHT = 300,
+} eGPBrush_Presets;
+
+/* BrushGpencilSettings->flag */
+typedef enum eGPDbrush_Flag {
+ /* brush use pressure */
+ GP_BRUSH_USE_PRESSURE = (1 << 0),
+ /* brush use pressure for alpha factor */
+ GP_BRUSH_USE_STRENGTH_PRESSURE = (1 << 1),
+ /* brush use pressure for alpha factor */
+ GP_BRUSH_USE_JITTER_PRESSURE = (1 << 2),
+ /* fill hide transparent */
+ GP_BRUSH_FILL_HIDE = (1 << 6),
+ /* show fill help lines */
+ GP_BRUSH_FILL_SHOW_HELPLINES = (1 << 7),
+ /* lazy mouse */
+ GP_BRUSH_STABILIZE_MOUSE = (1 << 8),
+ /* lazy mouse override (internal only) */
+ GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9),
+ /* default eraser brush for quick switch */
+ GP_BRUSH_DEFAULT_ERASER = (1 << 10),
+ /* settings group */
+ GP_BRUSH_GROUP_SETTINGS = (1 << 11),
+ /* Random settings group */
+ GP_BRUSH_GROUP_RANDOM = (1 << 12),
+ /* Keep material assigned to brush */
+ GP_BRUSH_MATERIAL_PINNED = (1 << 13),
+ /* Do not show fill color while drawing (no lasso mode) */
+ GP_BRUSH_DISSABLE_LASSO = (1 << 14),
+ /* Do not erase strokes oLcluded */
+ GP_BRUSH_OCCLUDE_ERASER = (1 << 15),
+ /* Post process trim stroke */
+ GP_BRUSH_TRIM_STROKE = (1 << 16),
+} eGPDbrush_Flag;
+
+typedef enum eGPDbrush_Flag2 {
+ /* Brush use random Hue at stroke level */
+ GP_BRUSH_USE_HUE_AT_STROKE = (1 << 0),
+ /* Brush use random Saturation at stroke level */
+ GP_BRUSH_USE_SAT_AT_STROKE = (1 << 1),
+ /* Brush use random Value at stroke level */
+ GP_BRUSH_USE_VAL_AT_STROKE = (1 << 2),
+ /* Brush use random Pressure at stroke level */
+ GP_BRUSH_USE_PRESS_AT_STROKE = (1 << 3),
+ /* Brush use random Strength at stroke level */
+ GP_BRUSH_USE_STRENGTH_AT_STROKE = (1 << 4),
+ /* Brush use random UV at stroke level */
+ GP_BRUSH_USE_UV_AT_STROKE = (1 << 5),
+ /* Brush use Hue random pressure */
+ GP_BRUSH_USE_HUE_RAND_PRESS = (1 << 6),
+ /* Brush use Saturation random pressure */
+ GP_BRUSH_USE_SAT_RAND_PRESS = (1 << 7),
+ /* Brush use Value random pressure */
+ GP_BRUSH_USE_VAL_RAND_PRESS = (1 << 8),
+ /* Brush use Pressure random pressure */
+ GP_BRUSH_USE_PRESSURE_RAND_PRESS = (1 << 9),
+ /* Brush use Strength random pressure */
+ GP_BRUSH_USE_STRENGTH_RAND_PRESS = (1 << 10),
+ /* Brush use UV random pressure */
+ GP_BRUSH_USE_UV_RAND_PRESS = (1 << 11),
+} eGPDbrush_Flag2;
+
+/* BrushGpencilSettings->gp_fill_draw_mode */
+typedef enum eGP_FillDrawModes {
+ GP_FILL_DMODE_BOTH = 0,
+ GP_FILL_DMODE_STROKE = 1,
+ GP_FILL_DMODE_CONTROL = 2,
+} eGP_FillDrawModes;
+
+/* BrushGpencilSettings->fill_layer_mode */
+typedef enum eGP_FillLayerModes {
+ GP_FILL_GPLMODE_VISIBLE = 0,
+ GP_FILL_GPLMODE_ACTIVE = 1,
+ GP_FILL_GPLMODE_ALL_ABOVE = 2,
+ GP_FILL_GPLMODE_ALL_BELOW = 3,
+ GP_FILL_GPLMODE_ABOVE = 4,
+ GP_FILL_GPLMODE_BELOW = 5,
+} eGP_FillLayerModes;
+
+/* BrushGpencilSettings->gp_eraser_mode */
+typedef enum eGP_BrushEraserMode {
+ GP_BRUSH_ERASER_SOFT = 0,
+ GP_BRUSH_ERASER_HARD = 1,
+ GP_BRUSH_ERASER_STROKE = 2,
+} eGP_BrushEraserMode;
+
+/* BrushGpencilSettings->brush_draw_mode */
+typedef enum eGP_BrushMode {
+ GP_BRUSH_MODE_ACTIVE = 0,
+ GP_BRUSH_MODE_MATERIAL = 1,
+ GP_BRUSH_MODE_VERTEXCOLOR = 2,
+} eGP_BrushMode;
+
+/* BrushGpencilSettings default brush icons */
+typedef enum eGP_BrushIcons {
+ GP_BRUSH_ICON_PENCIL = 1,
+ GP_BRUSH_ICON_PEN = 2,
+ GP_BRUSH_ICON_INK = 3,
+ GP_BRUSH_ICON_INKNOISE = 4,
+ GP_BRUSH_ICON_BLOCK = 5,
+ GP_BRUSH_ICON_MARKER = 6,
+ GP_BRUSH_ICON_FILL = 7,
+ GP_BRUSH_ICON_ERASE_SOFT = 8,
+ GP_BRUSH_ICON_ERASE_HARD = 9,
+ GP_BRUSH_ICON_ERASE_STROKE = 10,
+ GP_BRUSH_ICON_AIRBRUSH = 11,
+ GP_BRUSH_ICON_CHISEL = 12,
+ GP_BRUSH_ICON_TINT = 13,
+ GP_BRUSH_ICON_VERTEX_DRAW = 14,
+ GP_BRUSH_ICON_VERTEX_BLUR = 15,
+ GP_BRUSH_ICON_VERTEX_AVERAGE = 16,
+ GP_BRUSH_ICON_VERTEX_SMEAR = 17,
+ GP_BRUSH_ICON_VERTEX_REPLACE = 18,
+ GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19,
+ GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20,
+ GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21,
+ GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22,
+ GP_BRUSH_ICON_GPBRUSH_GRAB = 23,
+ GP_BRUSH_ICON_GPBRUSH_PUSH = 24,
+ GP_BRUSH_ICON_GPBRUSH_TWIST = 25,
+ GP_BRUSH_ICON_GPBRUSH_PINCH = 26,
+ GP_BRUSH_ICON_GPBRUSH_CLONE = 27,
+ GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28,
+} eGP_BrushIcons;
+
+typedef enum eBrushCurvePreset {
+ BRUSH_CURVE_CUSTOM = 0,
+ BRUSH_CURVE_SMOOTH = 1,
+ BRUSH_CURVE_SPHERE = 2,
+ BRUSH_CURVE_ROOT = 3,
+ BRUSH_CURVE_SHARP = 4,
+ BRUSH_CURVE_LIN = 5,
+ BRUSH_CURVE_POW4 = 6,
+ BRUSH_CURVE_INVSQUARE = 7,
+ BRUSH_CURVE_CONSTANT = 8,
+ BRUSH_CURVE_SMOOTHER = 9,
+} eBrushCurvePreset;
+
+typedef enum eBrushDeformTarget {
+ BRUSH_DEFORM_TARGET_GEOMETRY = 0,
+ BRUSH_DEFORM_TARGET_CLOTH_SIM = 1,
+} eBrushDeformTarget;
+
+typedef enum eBrushElasticDeformType {
+ BRUSH_ELASTIC_DEFORM_GRAB = 0,
+ BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1,
+ BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE = 2,
+ BRUSH_ELASTIC_DEFORM_SCALE = 3,
+ BRUSH_ELASTIC_DEFORM_TWIST = 4,
+} eBrushElasticDeformType;
+
+typedef enum eBrushClothDeformType {
+ BRUSH_CLOTH_DEFORM_DRAG = 0,
+ BRUSH_CLOTH_DEFORM_PUSH = 1,
+ BRUSH_CLOTH_DEFORM_GRAB = 2,
+ BRUSH_CLOTH_DEFORM_PINCH_POINT = 3,
+ BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4,
+ BRUSH_CLOTH_DEFORM_INFLATE = 5,
+ BRUSH_CLOTH_DEFORM_EXPAND = 6,
+ BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7,
+} eBrushClothDeformType;
+
+typedef enum eBrushSmoothDeformType {
+ BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0,
+ BRUSH_SMOOTH_DEFORM_SURFACE = 1,
+} eBrushSmoothDeformType;
+
+typedef enum eBrushClothForceFalloffType {
+ BRUSH_CLOTH_FORCE_FALLOFF_RADIAL = 0,
+ BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1,
+} eBrushClothForceFalloffType;
+
+typedef enum eBrushClothSimulationAreaType {
+ BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0,
+ BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1,
+ BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC = 2,
+} eBrushClothSimulationAreaType;
+
+typedef enum eBrushPoseDeformType {
+ BRUSH_POSE_DEFORM_ROTATE_TWIST = 0,
+ BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1,
+ BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2,
+} eBrushPoseDeformType;
+
+typedef enum eBrushPoseOriginType {
+ BRUSH_POSE_ORIGIN_TOPOLOGY = 0,
+ BRUSH_POSE_ORIGIN_FACE_SETS = 1,
+ BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2,
+} eBrushPoseOriginType;
+
+typedef enum eBrushSmearDeformType {
+ BRUSH_SMEAR_DEFORM_DRAG = 0,
+ BRUSH_SMEAR_DEFORM_PINCH = 1,
+ BRUSH_SMEAR_DEFORM_EXPAND = 2,
+} eBrushSmearDeformType;
+
+typedef enum eBrushSlideDeformType {
+ BRUSH_SLIDE_DEFORM_DRAG = 0,
+ BRUSH_SLIDE_DEFORM_PINCH = 1,
+ BRUSH_SLIDE_DEFORM_EXPAND = 2,
+} eBrushSlideDeformType;
+
+typedef enum eBrushBoundaryDeformType {
+ BRUSH_BOUNDARY_DEFORM_BEND = 0,
+ BRUSH_BOUNDARY_DEFORM_EXPAND = 1,
+ BRUSH_BOUNDARY_DEFORM_INFLATE = 2,
+ BRUSH_BOUNDARY_DEFORM_GRAB = 3,
+ BRUSH_BOUNDARY_DEFORM_TWIST = 4,
+ BRUSH_BOUNDARY_DEFORM_SMOOTH = 5,
+} eBrushBushBoundaryDeformType;
+
+typedef enum eBrushBoundaryFalloffType {
+ BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0,
+ BRUSH_BOUNDARY_FALLOFF_RADIUS = 1,
+ BRUSH_BOUNDARY_FALLOFF_LOOP = 2,
+ BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3,
+} eBrushBoundaryFalloffType;
+
+typedef enum eBrushSnakeHookDeformType {
+ BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0,
+ BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1,
+} eBrushSnakeHookDeformType;
+
+/* Gpencilsettings.Vertex_mode */
+typedef enum eGp_Vertex_Mode {
+ /* Affect to Stroke only. */
+ GPPAINT_MODE_STROKE = 0,
+ /* Affect to Fill only. */
+ GPPAINT_MODE_FILL = 1,
+ /* Affect to both. */
+ GPPAINT_MODE_BOTH = 2,
+} eGp_Vertex_Mode;
+
+/* sculpt_flag */
+typedef enum eGP_Sculpt_Flag {
+ /* invert the effect of the brush */
+ GP_SCULPT_FLAG_INVERT = (1 << 0),
+ /* smooth brush affects pressure values as well */
+ GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2),
+ /* temporary invert action */
+ GP_SCULPT_FLAG_TMP_INVERT = (1 << 3),
+} eGP_Sculpt_Flag;
+
+/* sculpt_mode_flag */
+typedef enum eGP_Sculpt_Mode_Flag {
+ /* apply brush to position */
+ GP_SCULPT_FLAGMODE_APPLY_POSITION = (1 << 0),
+ /* apply brush to strength */
+ GP_SCULPT_FLAGMODE_APPLY_STRENGTH = (1 << 1),
+ /* apply brush to thickness */
+ GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2),
+ /* apply brush to uv data */
+ GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3),
+} eGP_Sculpt_Mode_Flag;
+
+typedef enum eAutomasking_flag {
+ BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0),
+ BRUSH_AUTOMASKING_FACE_SETS = (1 << 1),
+ BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2),
+ BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3),
+} eAutomasking_flag;
+
+typedef enum ePaintBrush_flag {
+ BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0),
+ BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1),
+ BRUSH_PAINT_FLOW_PRESSURE = (1 << 2),
+ BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3),
+ BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4),
+ BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5),
+ BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6),
+ BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7),
+ BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8),
+ BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9),
+} ePaintBrush_flag;
+
+/* Brush.gradient_source */
+typedef enum eBrushGradientSourceStroke {
+ BRUSH_GRADIENT_PRESSURE = 0, /* gradient from pressure */
+ BRUSH_GRADIENT_SPACING_REPEAT = 1, /* gradient from spacing */
+ BRUSH_GRADIENT_SPACING_CLAMP = 2, /* gradient from spacing */
+} eBrushGradientSourceStroke;
+
+typedef enum eBrushGradientSourceFill {
+ BRUSH_GRADIENT_LINEAR = 0, /* gradient from pressure */
+ BRUSH_GRADIENT_RADIAL = 1, /* gradient from spacing */
+} eBrushGradientSourceFill;
+
+/* Brush.flag */
+typedef enum eBrushFlags {
+ BRUSH_AIRBRUSH = (1 << 0),
+ BRUSH_INVERT_TO_SCRAPE_FILL = (1 << 1),
+ BRUSH_ALPHA_PRESSURE = (1 << 2),
+ BRUSH_SIZE_PRESSURE = (1 << 3),
+ BRUSH_JITTER_PRESSURE = (1 << 4),
+ BRUSH_SPACING_PRESSURE = (1 << 5),
+ BRUSH_ORIGINAL_PLANE = (1 << 6),
+ BRUSH_GRAB_ACTIVE_VERTEX = (1 << 7),
+ BRUSH_ANCHORED = (1 << 8),
+ BRUSH_DIR_IN = (1 << 9),
+ BRUSH_SPACE = (1 << 10),
+ BRUSH_SMOOTH_STROKE = (1 << 11),
+ BRUSH_PERSISTENT = (1 << 12),
+ BRUSH_ACCUMULATE = (1 << 13),
+ BRUSH_LOCK_ALPHA = (1 << 14),
+ BRUSH_ORIGINAL_NORMAL = (1 << 15),
+ BRUSH_OFFSET_PRESSURE = (1 << 16),
+ BRUSH_SCENE_SPACING = (1 << 17),
+ BRUSH_SPACE_ATTEN = (1 << 18),
+ BRUSH_ADAPTIVE_SPACE = (1 << 19),
+ BRUSH_LOCK_SIZE = (1 << 20),
+ BRUSH_USE_GRADIENT = (1 << 21),
+ BRUSH_EDGE_TO_EDGE = (1 << 22),
+ BRUSH_DRAG_DOT = (1 << 23),
+ BRUSH_INVERSE_SMOOTH_PRESSURE = (1 << 24),
+ BRUSH_FRONTFACE_FALLOFF = (1 << 25),
+ BRUSH_PLANE_TRIM = (1 << 26),
+ BRUSH_FRONTFACE = (1 << 27),
+ BRUSH_CUSTOM_ICON = (1 << 28),
+ BRUSH_LINE = (1 << 29),
+ BRUSH_ABSOLUTE_JITTER = (1 << 30),
+ BRUSH_CURVE = (1u << 31),
+} eBrushFlags;
+
+/* Brush.sampling_flag */
+typedef enum eBrushSamplingFlags {
+ BRUSH_PAINT_ANTIALIASING = (1 << 0),
+} eBrushSamplingFlags;
+
+/* Brush.flag2 */
+typedef enum eBrushFlags2 {
+ BRUSH_MULTIPLANE_SCRAPE_DYNAMIC = (1 << 0),
+ BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1),
+ BRUSH_POSE_IK_ANCHORED = (1 << 2),
+ BRUSH_USE_CONNECTED_ONLY = (1 << 3),
+ BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4),
+ BRUSH_POSE_USE_LOCK_ROTATION = (1 << 5),
+ BRUSH_CLOTH_USE_COLLISION = (1 << 6),
+ BRUSH_AREA_RADIUS_PRESSURE = (1 << 7),
+ BRUSH_GRAB_SILHOUETTE = (1 << 8),
+} eBrushFlags2;
+
+typedef enum {
+ BRUSH_MASK_PRESSURE_RAMP = (1 << 1),
+ BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2),
+} BrushMaskPressureFlags;
+
+/* Brush.overlay_flags */
+typedef enum eOverlayFlags {
+ BRUSH_OVERLAY_CURSOR = (1),
+ BRUSH_OVERLAY_PRIMARY = (1 << 1),
+ BRUSH_OVERLAY_SECONDARY = (1 << 2),
+ BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE = (1 << 3),
+ BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE = (1 << 4),
+ BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE = (1 << 5),
+} eOverlayFlags;
+
+#define BRUSH_OVERLAY_OVERRIDE_MASK \
+ (BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE | BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE | \
+ BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE)
+
+/* Brush.sculpt_tool */
+typedef enum eBrushSculptTool {
+ SCULPT_TOOL_DRAW = 1,
+ SCULPT_TOOL_SMOOTH = 2,
+ SCULPT_TOOL_PINCH = 3,
+ SCULPT_TOOL_INFLATE = 4,
+ SCULPT_TOOL_GRAB = 5,
+ SCULPT_TOOL_LAYER = 6,
+ SCULPT_TOOL_FLATTEN = 7,
+ SCULPT_TOOL_CLAY = 8,
+ SCULPT_TOOL_FILL = 9,
+ SCULPT_TOOL_SCRAPE = 10,
+ SCULPT_TOOL_NUDGE = 11,
+ SCULPT_TOOL_THUMB = 12,
+ SCULPT_TOOL_SNAKE_HOOK = 13,
+ SCULPT_TOOL_ROTATE = 14,
+ SCULPT_TOOL_SIMPLIFY = 15,
+ SCULPT_TOOL_CREASE = 16,
+ SCULPT_TOOL_BLOB = 17,
+ SCULPT_TOOL_CLAY_STRIPS = 18,
+ SCULPT_TOOL_MASK = 19,
+ SCULPT_TOOL_DRAW_SHARP = 20,
+ SCULPT_TOOL_ELASTIC_DEFORM = 21,
+ SCULPT_TOOL_POSE = 22,
+ SCULPT_TOOL_MULTIPLANE_SCRAPE = 23,
+ SCULPT_TOOL_SLIDE_RELAX = 24,
+ SCULPT_TOOL_CLAY_THUMB = 25,
+ SCULPT_TOOL_CLOTH = 26,
+ SCULPT_TOOL_DRAW_FACE_SETS = 27,
+ SCULPT_TOOL_PAINT = 28,
+ SCULPT_TOOL_SMEAR = 29,
+ SCULPT_TOOL_BOUNDARY = 30,
+ SCULPT_TOOL_DISPLACEMENT_ERASER = 31,
+ SCULPT_TOOL_DISPLACEMENT_SMEAR = 32,
+} eBrushSculptTool;
+
+/* Brush.uv_sculpt_tool */
+typedef enum eBrushUVSculptTool {
+ UV_SCULPT_TOOL_GRAB = 0,
+ UV_SCULPT_TOOL_RELAX = 1,
+ UV_SCULPT_TOOL_PINCH = 2,
+} eBrushUVSculptTool;
+
+/** When #BRUSH_ACCUMULATE is used */
+#define SCULPT_TOOL_HAS_ACCUMULATE(t) \
+ ELEM(t, \
+ SCULPT_TOOL_DRAW, \
+ SCULPT_TOOL_DRAW_SHARP, \
+ SCULPT_TOOL_SLIDE_RELAX, \
+ SCULPT_TOOL_CREASE, \
+ SCULPT_TOOL_BLOB, \
+ SCULPT_TOOL_INFLATE, \
+ SCULPT_TOOL_CLAY, \
+ SCULPT_TOOL_CLAY_STRIPS, \
+ SCULPT_TOOL_CLAY_THUMB, \
+ SCULPT_TOOL_ROTATE, \
+ SCULPT_TOOL_SCRAPE, \
+ SCULPT_TOOL_FLATTEN)
+
+#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \
+ ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM)
+
+#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK)
+
+#define SCULPT_TOOL_HAS_DYNTOPO(t) \
+ (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \
+ SCULPT_TOOL_GRAB, \
+ SCULPT_TOOL_ROTATE, \
+ SCULPT_TOOL_CLOTH, \
+ SCULPT_TOOL_THUMB, \
+ SCULPT_TOOL_LAYER, \
+ SCULPT_TOOL_DISPLACEMENT_ERASER, \
+ SCULPT_TOOL_DRAW_SHARP, \
+ SCULPT_TOOL_SLIDE_RELAX, \
+ SCULPT_TOOL_ELASTIC_DEFORM, \
+ SCULPT_TOOL_BOUNDARY, \
+ SCULPT_TOOL_POSE, \
+ SCULPT_TOOL_DRAW_FACE_SETS, \
+ SCULPT_TOOL_PAINT, \
+ SCULPT_TOOL_SMEAR, \
+\
+ /* These brushes could handle dynamic topology, \ \
+ * but user feedback indicates it's better not to */ \
+ SCULPT_TOOL_SMOOTH, \
+ SCULPT_TOOL_MASK) == 0)
+
+#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \
+ (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \
+ SCULPT_TOOL_GRAB, \
+ SCULPT_TOOL_ROTATE, \
+ SCULPT_TOOL_THUMB, \
+ SCULPT_TOOL_DRAW_SHARP, \
+ SCULPT_TOOL_DISPLACEMENT_ERASER, \
+ SCULPT_TOOL_SLIDE_RELAX, \
+ SCULPT_TOOL_MASK) == 0)
+
+/* ImagePaintSettings.tool */
+typedef enum eBrushImagePaintTool {
+ PAINT_TOOL_DRAW = 0,
+ PAINT_TOOL_SOFTEN = 1,
+ PAINT_TOOL_SMEAR = 2,
+ PAINT_TOOL_CLONE = 3,
+ PAINT_TOOL_FILL = 4,
+ PAINT_TOOL_MASK = 5,
+} eBrushImagePaintTool;
+
+typedef enum eBrushVertexPaintTool {
+ VPAINT_TOOL_DRAW = 0,
+ VPAINT_TOOL_BLUR = 1,
+ VPAINT_TOOL_AVERAGE = 2,
+ VPAINT_TOOL_SMEAR = 3,
+} eBrushVertexPaintTool;
+
+typedef enum eBrushWeightPaintTool {
+ WPAINT_TOOL_DRAW = 0,
+ WPAINT_TOOL_BLUR = 1,
+ WPAINT_TOOL_AVERAGE = 2,
+ WPAINT_TOOL_SMEAR = 3,
+} eBrushWeightPaintTool;
+
+/* BrushGpencilSettings->brush type */
+typedef enum eBrushGPaintTool {
+ GPAINT_TOOL_DRAW = 0,
+ GPAINT_TOOL_FILL = 1,
+ GPAINT_TOOL_ERASE = 2,
+ GPAINT_TOOL_TINT = 3,
+} eBrushGPaintTool;
+
+/* BrushGpencilSettings->brush type */
+typedef enum eBrushGPVertexTool {
+ GPVERTEX_TOOL_DRAW = 0,
+ GPVERTEX_TOOL_BLUR = 1,
+ GPVERTEX_TOOL_AVERAGE = 2,
+ GPVERTEX_TOOL_TINT = 3,
+ GPVERTEX_TOOL_SMEAR = 4,
+ GPVERTEX_TOOL_REPLACE = 5,
+} eBrushGPVertexTool;
+
+/* BrushGpencilSettings->brush type */
+typedef enum eBrushGPSculptTool {
+ GPSCULPT_TOOL_SMOOTH = 0,
+ GPSCULPT_TOOL_THICKNESS = 1,
+ GPSCULPT_TOOL_STRENGTH = 2,
+ GPSCULPT_TOOL_GRAB = 3,
+ GPSCULPT_TOOL_PUSH = 4,
+ GPSCULPT_TOOL_TWIST = 5,
+ GPSCULPT_TOOL_PINCH = 6,
+ GPSCULPT_TOOL_RANDOMIZE = 7,
+ GPSCULPT_TOOL_CLONE = 8,
+} eBrushGPSculptTool;
+
+/* BrushGpencilSettings->brush type */
+typedef enum eBrushGPWeightTool {
+ GPWEIGHT_TOOL_DRAW = 0,
+} eBrushGPWeightTool;
+
+/* direction that the brush displaces along */
+enum {
+ SCULPT_DISP_DIR_AREA = 0,
+ SCULPT_DISP_DIR_VIEW = 1,
+ SCULPT_DISP_DIR_X = 2,
+ SCULPT_DISP_DIR_Y = 3,
+ SCULPT_DISP_DIR_Z = 4,
+};
+
+typedef enum {
+ BRUSH_MASK_DRAW = 0,
+ BRUSH_MASK_SMOOTH = 1,
+} BrushMaskTool;
+
+/* blur kernel types, Brush.blur_mode */
+typedef enum eBlurKernelType {
+ KERNEL_GAUSSIAN = 0,
+ KERNEL_BOX = 1,
+} eBlurKernelType;
+
+/* Brush.falloff_shape */
+enum {
+ PAINT_FALLOFF_SHAPE_SPHERE = 0,
+ PAINT_FALLOFF_SHAPE_TUBE = 1,
+};
+
+#define MAX_BRUSH_PIXEL_RADIUS 500
+#define GP_MAX_BRUSH_PIXEL_RADIUS 1000
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 7bd3c7d0117..1709ea5dc63 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -24,6 +24,7 @@
#pragma once
#include "DNA_ID.h"
+#include "DNA_brush_enums.h"
#include "DNA_curve_types.h"
#include "DNA_texture_types.h" /* for MTex */
@@ -144,321 +145,6 @@ typedef struct BrushGpencilSettings {
struct Material *material;
} BrushGpencilSettings;
-/* BrushGpencilSettings->preset_type.
- * Use a range for each group and not continuous values.*/
-typedef enum eGPBrush_Presets {
- GP_BRUSH_PRESET_UNKNOWN = 0,
-
- /* Draw 1-99. */
- GP_BRUSH_PRESET_AIRBRUSH = 1,
- GP_BRUSH_PRESET_INK_PEN = 2,
- GP_BRUSH_PRESET_INK_PEN_ROUGH = 3,
- GP_BRUSH_PRESET_MARKER_BOLD = 4,
- GP_BRUSH_PRESET_MARKER_CHISEL = 5,
- GP_BRUSH_PRESET_PEN = 6,
- GP_BRUSH_PRESET_PENCIL_SOFT = 7,
- GP_BRUSH_PRESET_PENCIL = 8,
- GP_BRUSH_PRESET_FILL_AREA = 9,
- GP_BRUSH_PRESET_ERASER_SOFT = 10,
- GP_BRUSH_PRESET_ERASER_HARD = 11,
- GP_BRUSH_PRESET_ERASER_POINT = 12,
- GP_BRUSH_PRESET_ERASER_STROKE = 13,
- GP_BRUSH_PRESET_TINT = 14,
-
- /* Vertex Paint 100-199. */
- GP_BRUSH_PRESET_VERTEX_DRAW = 100,
- GP_BRUSH_PRESET_VERTEX_BLUR = 101,
- GP_BRUSH_PRESET_VERTEX_AVERAGE = 102,
- GP_BRUSH_PRESET_VERTEX_SMEAR = 103,
- GP_BRUSH_PRESET_VERTEX_REPLACE = 104,
-
- /* Sculpt 200-299. */
- GP_BRUSH_PRESET_SMOOTH_STROKE = 200,
- GP_BRUSH_PRESET_STRENGTH_STROKE = 201,
- GP_BRUSH_PRESET_THICKNESS_STROKE = 202,
- GP_BRUSH_PRESET_GRAB_STROKE = 203,
- GP_BRUSH_PRESET_PUSH_STROKE = 204,
- GP_BRUSH_PRESET_TWIST_STROKE = 205,
- GP_BRUSH_PRESET_PINCH_STROKE = 206,
- GP_BRUSH_PRESET_RANDOMIZE_STROKE = 207,
- GP_BRUSH_PRESET_CLONE_STROKE = 208,
-
- /* Weight Paint 300-399. */
- GP_BRUSH_PRESET_DRAW_WEIGHT = 300,
-} eGPBrush_Presets;
-
-/* BrushGpencilSettings->flag */
-typedef enum eGPDbrush_Flag {
- /* brush use pressure */
- GP_BRUSH_USE_PRESSURE = (1 << 0),
- /* brush use pressure for alpha factor */
- GP_BRUSH_USE_STRENGTH_PRESSURE = (1 << 1),
- /* brush use pressure for alpha factor */
- GP_BRUSH_USE_JITTER_PRESSURE = (1 << 2),
- /* fill hide transparent */
- GP_BRUSH_FILL_HIDE = (1 << 6),
- /* show fill help lines */
- GP_BRUSH_FILL_SHOW_HELPLINES = (1 << 7),
- /* lazy mouse */
- GP_BRUSH_STABILIZE_MOUSE = (1 << 8),
- /* lazy mouse override (internal only) */
- GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9),
- /* default eraser brush for quick switch */
- GP_BRUSH_DEFAULT_ERASER = (1 << 10),
- /* settings group */
- GP_BRUSH_GROUP_SETTINGS = (1 << 11),
- /* Random settings group */
- GP_BRUSH_GROUP_RANDOM = (1 << 12),
- /* Keep material assigned to brush */
- GP_BRUSH_MATERIAL_PINNED = (1 << 13),
- /* Do not show fill color while drawing (no lasso mode) */
- GP_BRUSH_DISSABLE_LASSO = (1 << 14),
- /* Do not erase strokes oLcluded */
- GP_BRUSH_OCCLUDE_ERASER = (1 << 15),
- /* Post process trim stroke */
- GP_BRUSH_TRIM_STROKE = (1 << 16),
-} eGPDbrush_Flag;
-
-typedef enum eGPDbrush_Flag2 {
- /* Brush use random Hue at stroke level */
- GP_BRUSH_USE_HUE_AT_STROKE = (1 << 0),
- /* Brush use random Saturation at stroke level */
- GP_BRUSH_USE_SAT_AT_STROKE = (1 << 1),
- /* Brush use random Value at stroke level */
- GP_BRUSH_USE_VAL_AT_STROKE = (1 << 2),
- /* Brush use random Pressure at stroke level */
- GP_BRUSH_USE_PRESS_AT_STROKE = (1 << 3),
- /* Brush use random Strength at stroke level */
- GP_BRUSH_USE_STRENGTH_AT_STROKE = (1 << 4),
- /* Brush use random UV at stroke level */
- GP_BRUSH_USE_UV_AT_STROKE = (1 << 5),
- /* Brush use Hue random pressure */
- GP_BRUSH_USE_HUE_RAND_PRESS = (1 << 6),
- /* Brush use Saturation random pressure */
- GP_BRUSH_USE_SAT_RAND_PRESS = (1 << 7),
- /* Brush use Value random pressure */
- GP_BRUSH_USE_VAL_RAND_PRESS = (1 << 8),
- /* Brush use Pressure random pressure */
- GP_BRUSH_USE_PRESSURE_RAND_PRESS = (1 << 9),
- /* Brush use Strength random pressure */
- GP_BRUSH_USE_STRENGTH_RAND_PRESS = (1 << 10),
- /* Brush use UV random pressure */
- GP_BRUSH_USE_UV_RAND_PRESS = (1 << 11),
-} eGPDbrush_Flag2;
-
-/* BrushGpencilSettings->gp_fill_draw_mode */
-typedef enum eGP_FillDrawModes {
- GP_FILL_DMODE_BOTH = 0,
- GP_FILL_DMODE_STROKE = 1,
- GP_FILL_DMODE_CONTROL = 2,
-} eGP_FillDrawModes;
-
-/* BrushGpencilSettings->fill_layer_mode */
-typedef enum eGP_FillLayerModes {
- GP_FILL_GPLMODE_VISIBLE = 0,
- GP_FILL_GPLMODE_ACTIVE = 1,
- GP_FILL_GPLMODE_ALL_ABOVE = 2,
- GP_FILL_GPLMODE_ALL_BELOW = 3,
- GP_FILL_GPLMODE_ABOVE = 4,
- GP_FILL_GPLMODE_BELOW = 5,
-} eGP_FillLayerModes;
-
-/* BrushGpencilSettings->gp_eraser_mode */
-typedef enum eGP_BrushEraserMode {
- GP_BRUSH_ERASER_SOFT = 0,
- GP_BRUSH_ERASER_HARD = 1,
- GP_BRUSH_ERASER_STROKE = 2,
-} eGP_BrushEraserMode;
-
-/* BrushGpencilSettings->brush_draw_mode */
-typedef enum eGP_BrushMode {
- GP_BRUSH_MODE_ACTIVE = 0,
- GP_BRUSH_MODE_MATERIAL = 1,
- GP_BRUSH_MODE_VERTEXCOLOR = 2,
-} eGP_BrushMode;
-
-/* BrushGpencilSettings default brush icons */
-typedef enum eGP_BrushIcons {
- GP_BRUSH_ICON_PENCIL = 1,
- GP_BRUSH_ICON_PEN = 2,
- GP_BRUSH_ICON_INK = 3,
- GP_BRUSH_ICON_INKNOISE = 4,
- GP_BRUSH_ICON_BLOCK = 5,
- GP_BRUSH_ICON_MARKER = 6,
- GP_BRUSH_ICON_FILL = 7,
- GP_BRUSH_ICON_ERASE_SOFT = 8,
- GP_BRUSH_ICON_ERASE_HARD = 9,
- GP_BRUSH_ICON_ERASE_STROKE = 10,
- GP_BRUSH_ICON_AIRBRUSH = 11,
- GP_BRUSH_ICON_CHISEL = 12,
- GP_BRUSH_ICON_TINT = 13,
- GP_BRUSH_ICON_VERTEX_DRAW = 14,
- GP_BRUSH_ICON_VERTEX_BLUR = 15,
- GP_BRUSH_ICON_VERTEX_AVERAGE = 16,
- GP_BRUSH_ICON_VERTEX_SMEAR = 17,
- GP_BRUSH_ICON_VERTEX_REPLACE = 18,
- GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19,
- GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20,
- GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21,
- GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22,
- GP_BRUSH_ICON_GPBRUSH_GRAB = 23,
- GP_BRUSH_ICON_GPBRUSH_PUSH = 24,
- GP_BRUSH_ICON_GPBRUSH_TWIST = 25,
- GP_BRUSH_ICON_GPBRUSH_PINCH = 26,
- GP_BRUSH_ICON_GPBRUSH_CLONE = 27,
- GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28,
-} eGP_BrushIcons;
-
-typedef enum eBrushCurvePreset {
- BRUSH_CURVE_CUSTOM = 0,
- BRUSH_CURVE_SMOOTH = 1,
- BRUSH_CURVE_SPHERE = 2,
- BRUSH_CURVE_ROOT = 3,
- BRUSH_CURVE_SHARP = 4,
- BRUSH_CURVE_LIN = 5,
- BRUSH_CURVE_POW4 = 6,
- BRUSH_CURVE_INVSQUARE = 7,
- BRUSH_CURVE_CONSTANT = 8,
- BRUSH_CURVE_SMOOTHER = 9,
-} eBrushCurvePreset;
-
-typedef enum eBrushDeformTarget {
- BRUSH_DEFORM_TARGET_GEOMETRY = 0,
- BRUSH_DEFORM_TARGET_CLOTH_SIM = 1,
-} eBrushDeformTarget;
-
-typedef enum eBrushElasticDeformType {
- BRUSH_ELASTIC_DEFORM_GRAB = 0,
- BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1,
- BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE = 2,
- BRUSH_ELASTIC_DEFORM_SCALE = 3,
- BRUSH_ELASTIC_DEFORM_TWIST = 4,
-} eBrushElasticDeformType;
-
-typedef enum eBrushClothDeformType {
- BRUSH_CLOTH_DEFORM_DRAG = 0,
- BRUSH_CLOTH_DEFORM_PUSH = 1,
- BRUSH_CLOTH_DEFORM_GRAB = 2,
- BRUSH_CLOTH_DEFORM_PINCH_POINT = 3,
- BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4,
- BRUSH_CLOTH_DEFORM_INFLATE = 5,
- BRUSH_CLOTH_DEFORM_EXPAND = 6,
- BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7,
-} eBrushClothDeformType;
-
-typedef enum eBrushSmoothDeformType {
- BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0,
- BRUSH_SMOOTH_DEFORM_SURFACE = 1,
-} eBrushSmoothDeformType;
-
-typedef enum eBrushClothForceFalloffType {
- BRUSH_CLOTH_FORCE_FALLOFF_RADIAL = 0,
- BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1,
-} eBrushClothForceFalloffType;
-
-typedef enum eBrushClothSimulationAreaType {
- BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0,
- BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1,
- BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC = 2,
-} eBrushClothSimulationAreaType;
-
-typedef enum eBrushPoseDeformType {
- BRUSH_POSE_DEFORM_ROTATE_TWIST = 0,
- BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1,
- BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2,
-} eBrushPoseDeformType;
-
-typedef enum eBrushPoseOriginType {
- BRUSH_POSE_ORIGIN_TOPOLOGY = 0,
- BRUSH_POSE_ORIGIN_FACE_SETS = 1,
- BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2,
-} eBrushPoseOriginType;
-
-typedef enum eBrushSmearDeformType {
- BRUSH_SMEAR_DEFORM_DRAG = 0,
- BRUSH_SMEAR_DEFORM_PINCH = 1,
- BRUSH_SMEAR_DEFORM_EXPAND = 2,
-} eBrushSmearDeformType;
-
-typedef enum eBrushSlideDeformType {
- BRUSH_SLIDE_DEFORM_DRAG = 0,
- BRUSH_SLIDE_DEFORM_PINCH = 1,
- BRUSH_SLIDE_DEFORM_EXPAND = 2,
-} eBrushSlideDeformType;
-
-typedef enum eBrushBoundaryDeformType {
- BRUSH_BOUNDARY_DEFORM_BEND = 0,
- BRUSH_BOUNDARY_DEFORM_EXPAND = 1,
- BRUSH_BOUNDARY_DEFORM_INFLATE = 2,
- BRUSH_BOUNDARY_DEFORM_GRAB = 3,
- BRUSH_BOUNDARY_DEFORM_TWIST = 4,
- BRUSH_BOUNDARY_DEFORM_SMOOTH = 5,
-} eBrushBushBoundaryDeformType;
-
-typedef enum eBrushBoundaryFalloffType {
- BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0,
- BRUSH_BOUNDARY_FALLOFF_RADIUS = 1,
- BRUSH_BOUNDARY_FALLOFF_LOOP = 2,
- BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3,
-} eBrushBoundaryFalloffType;
-
-typedef enum eBrushSnakeHookDeformType {
- BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0,
- BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1,
-} eBrushSnakeHookDeformType;
-
-/* Gpencilsettings.Vertex_mode */
-typedef enum eGp_Vertex_Mode {
- /* Affect to Stroke only. */
- GPPAINT_MODE_STROKE = 0,
- /* Affect to Fill only. */
- GPPAINT_MODE_FILL = 1,
- /* Affect to both. */
- GPPAINT_MODE_BOTH = 2,
-} eGp_Vertex_Mode;
-
-/* sculpt_flag */
-typedef enum eGP_Sculpt_Flag {
- /* invert the effect of the brush */
- GP_SCULPT_FLAG_INVERT = (1 << 0),
- /* smooth brush affects pressure values as well */
- GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2),
- /* temporary invert action */
- GP_SCULPT_FLAG_TMP_INVERT = (1 << 3),
-} eGP_Sculpt_Flag;
-
-/* sculpt_mode_flag */
-typedef enum eGP_Sculpt_Mode_Flag {
- /* apply brush to position */
- GP_SCULPT_FLAGMODE_APPLY_POSITION = (1 << 0),
- /* apply brush to strength */
- GP_SCULPT_FLAGMODE_APPLY_STRENGTH = (1 << 1),
- /* apply brush to thickness */
- GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2),
- /* apply brush to uv data */
- GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3),
-} eGP_Sculpt_Mode_Flag;
-
-typedef enum eAutomasking_flag {
- BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0),
- BRUSH_AUTOMASKING_FACE_SETS = (1 << 1),
- BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2),
- BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3),
-} eAutomasking_flag;
-
-typedef enum ePaintBrush_flag {
- BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0),
- BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1),
- BRUSH_PAINT_FLOW_PRESSURE = (1 << 2),
- BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3),
- BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4),
- BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5),
- BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6),
- BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7),
- BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8),
- BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9),
-} ePaintBrush_flag;
-
typedef struct Brush {
ID id;
@@ -687,7 +373,7 @@ typedef struct Brush {
typedef struct tPaletteColorHSV {
float rgb[3];
float value;
- float h;
+ float h;
float s;
float v;
} tPaletteColorHSV;
@@ -725,275 +411,6 @@ typedef struct PaintCurve {
int add_index;
} PaintCurve;
-/* Brush.gradient_source */
-typedef enum eBrushGradientSourceStroke {
- BRUSH_GRADIENT_PRESSURE = 0, /* gradient from pressure */
- BRUSH_GRADIENT_SPACING_REPEAT = 1, /* gradient from spacing */
- BRUSH_GRADIENT_SPACING_CLAMP = 2, /* gradient from spacing */
-} eBrushGradientSourceStroke;
-
-typedef enum eBrushGradientSourceFill {
- BRUSH_GRADIENT_LINEAR = 0, /* gradient from pressure */
- BRUSH_GRADIENT_RADIAL = 1, /* gradient from spacing */
-} eBrushGradientSourceFill;
-
-/* Brush.flag */
-typedef enum eBrushFlags {
- BRUSH_AIRBRUSH = (1 << 0),
- BRUSH_INVERT_TO_SCRAPE_FILL = (1 << 1),
- BRUSH_ALPHA_PRESSURE = (1 << 2),
- BRUSH_SIZE_PRESSURE = (1 << 3),
- BRUSH_JITTER_PRESSURE = (1 << 4),
- BRUSH_SPACING_PRESSURE = (1 << 5),
- BRUSH_ORIGINAL_PLANE = (1 << 6),
- BRUSH_GRAB_ACTIVE_VERTEX = (1 << 7),
- BRUSH_ANCHORED = (1 << 8),
- BRUSH_DIR_IN = (1 << 9),
- BRUSH_SPACE = (1 << 10),
- BRUSH_SMOOTH_STROKE = (1 << 11),
- BRUSH_PERSISTENT = (1 << 12),
- BRUSH_ACCUMULATE = (1 << 13),
- BRUSH_LOCK_ALPHA = (1 << 14),
- BRUSH_ORIGINAL_NORMAL = (1 << 15),
- BRUSH_OFFSET_PRESSURE = (1 << 16),
- BRUSH_SCENE_SPACING = (1 << 17),
- BRUSH_SPACE_ATTEN = (1 << 18),
- BRUSH_ADAPTIVE_SPACE = (1 << 19),
- BRUSH_LOCK_SIZE = (1 << 20),
- BRUSH_USE_GRADIENT = (1 << 21),
- BRUSH_EDGE_TO_EDGE = (1 << 22),
- BRUSH_DRAG_DOT = (1 << 23),
- BRUSH_INVERSE_SMOOTH_PRESSURE = (1 << 24),
- BRUSH_FRONTFACE_FALLOFF = (1 << 25),
- BRUSH_PLANE_TRIM = (1 << 26),
- BRUSH_FRONTFACE = (1 << 27),
- BRUSH_CUSTOM_ICON = (1 << 28),
- BRUSH_LINE = (1 << 29),
- BRUSH_ABSOLUTE_JITTER = (1 << 30),
- BRUSH_CURVE = (1u << 31),
-} eBrushFlags;
-
-/* Brush.sampling_flag */
-typedef enum eBrushSamplingFlags {
- BRUSH_PAINT_ANTIALIASING = (1 << 0),
-} eBrushSamplingFlags;
-
-/* Brush.flag2 */
-typedef enum eBrushFlags2 {
- BRUSH_MULTIPLANE_SCRAPE_DYNAMIC = (1 << 0),
- BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1),
- BRUSH_POSE_IK_ANCHORED = (1 << 2),
- BRUSH_USE_CONNECTED_ONLY = (1 << 3),
- BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4),
- BRUSH_POSE_USE_LOCK_ROTATION = (1 << 5),
- BRUSH_CLOTH_USE_COLLISION = (1 << 6),
- BRUSH_AREA_RADIUS_PRESSURE = (1 << 7),
- BRUSH_GRAB_SILHOUETTE = (1 << 8),
-} eBrushFlags2;
-
-typedef enum {
- BRUSH_MASK_PRESSURE_RAMP = (1 << 1),
- BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2),
-} BrushMaskPressureFlags;
-
-/* Brush.overlay_flags */
-typedef enum eOverlayFlags {
- BRUSH_OVERLAY_CURSOR = (1),
- BRUSH_OVERLAY_PRIMARY = (1 << 1),
- BRUSH_OVERLAY_SECONDARY = (1 << 2),
- BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE = (1 << 3),
- BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE = (1 << 4),
- BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE = (1 << 5),
-} eOverlayFlags;
-
-#define BRUSH_OVERLAY_OVERRIDE_MASK \
- (BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE | BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE | \
- BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE)
-
-/* Brush.sculpt_tool */
-typedef enum eBrushSculptTool {
- SCULPT_TOOL_DRAW = 1,
- SCULPT_TOOL_SMOOTH = 2,
- SCULPT_TOOL_PINCH = 3,
- SCULPT_TOOL_INFLATE = 4,
- SCULPT_TOOL_GRAB = 5,
- SCULPT_TOOL_LAYER = 6,
- SCULPT_TOOL_FLATTEN = 7,
- SCULPT_TOOL_CLAY = 8,
- SCULPT_TOOL_FILL = 9,
- SCULPT_TOOL_SCRAPE = 10,
- SCULPT_TOOL_NUDGE = 11,
- SCULPT_TOOL_THUMB = 12,
- SCULPT_TOOL_SNAKE_HOOK = 13,
- SCULPT_TOOL_ROTATE = 14,
- SCULPT_TOOL_SIMPLIFY = 15,
- SCULPT_TOOL_CREASE = 16,
- SCULPT_TOOL_BLOB = 17,
- SCULPT_TOOL_CLAY_STRIPS = 18,
- SCULPT_TOOL_MASK = 19,
- SCULPT_TOOL_DRAW_SHARP = 20,
- SCULPT_TOOL_ELASTIC_DEFORM = 21,
- SCULPT_TOOL_POSE = 22,
- SCULPT_TOOL_MULTIPLANE_SCRAPE = 23,
- SCULPT_TOOL_SLIDE_RELAX = 24,
- SCULPT_TOOL_CLAY_THUMB = 25,
- SCULPT_TOOL_CLOTH = 26,
- SCULPT_TOOL_DRAW_FACE_SETS = 27,
- SCULPT_TOOL_PAINT = 28,
- SCULPT_TOOL_SMEAR = 29,
- SCULPT_TOOL_BOUNDARY = 30,
- SCULPT_TOOL_DISPLACEMENT_ERASER = 31,
-} eBrushSculptTool;
-
-/* Brush.uv_sculpt_tool */
-typedef enum eBrushUVSculptTool {
- UV_SCULPT_TOOL_GRAB = 0,
- UV_SCULPT_TOOL_RELAX = 1,
- UV_SCULPT_TOOL_PINCH = 2,
-} eBrushUVSculptTool;
-
-/** When #BRUSH_ACCUMULATE is used */
-#define SCULPT_TOOL_HAS_ACCUMULATE(t) \
- ELEM(t, \
- SCULPT_TOOL_DRAW, \
- SCULPT_TOOL_DRAW_SHARP, \
- SCULPT_TOOL_SLIDE_RELAX, \
- SCULPT_TOOL_CREASE, \
- SCULPT_TOOL_BLOB, \
- SCULPT_TOOL_INFLATE, \
- SCULPT_TOOL_CLAY, \
- SCULPT_TOOL_CLAY_STRIPS, \
- SCULPT_TOOL_CLAY_THUMB, \
- SCULPT_TOOL_ROTATE, \
- SCULPT_TOOL_SCRAPE, \
- SCULPT_TOOL_FLATTEN)
-
-#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \
- ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM)
-
-#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK)
-
-#define SCULPT_TOOL_HAS_DYNTOPO(t) \
- (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \
- SCULPT_TOOL_GRAB, \
- SCULPT_TOOL_ROTATE, \
- SCULPT_TOOL_CLOTH, \
- SCULPT_TOOL_THUMB, \
- SCULPT_TOOL_LAYER, \
- SCULPT_TOOL_DISPLACEMENT_ERASER, \
- SCULPT_TOOL_DRAW_SHARP, \
- SCULPT_TOOL_SLIDE_RELAX, \
- SCULPT_TOOL_ELASTIC_DEFORM, \
- SCULPT_TOOL_BOUNDARY, \
- SCULPT_TOOL_POSE, \
- SCULPT_TOOL_DRAW_FACE_SETS, \
- SCULPT_TOOL_PAINT, \
- SCULPT_TOOL_SMEAR, \
-\
- /* These brushes could handle dynamic topology, \ \
- * but user feedback indicates it's better not to */ \
- SCULPT_TOOL_SMOOTH, \
- SCULPT_TOOL_MASK) == 0)
-
-#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \
- (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \
- SCULPT_TOOL_GRAB, \
- SCULPT_TOOL_ROTATE, \
- SCULPT_TOOL_THUMB, \
- SCULPT_TOOL_DRAW_SHARP, \
- SCULPT_TOOL_DISPLACEMENT_ERASER, \
- SCULPT_TOOL_SLIDE_RELAX, \
- SCULPT_TOOL_MASK) == 0)
-
-/* ImagePaintSettings.tool */
-typedef enum eBrushImagePaintTool {
- PAINT_TOOL_DRAW = 0,
- PAINT_TOOL_SOFTEN = 1,
- PAINT_TOOL_SMEAR = 2,
- PAINT_TOOL_CLONE = 3,
- PAINT_TOOL_FILL = 4,
- PAINT_TOOL_MASK = 5,
-} eBrushImagePaintTool;
-
-typedef enum eBrushVertexPaintTool {
- VPAINT_TOOL_DRAW = 0,
- VPAINT_TOOL_BLUR = 1,
- VPAINT_TOOL_AVERAGE = 2,
- VPAINT_TOOL_SMEAR = 3,
-} eBrushVertexPaintTool;
-
-typedef enum eBrushWeightPaintTool {
- WPAINT_TOOL_DRAW = 0,
- WPAINT_TOOL_BLUR = 1,
- WPAINT_TOOL_AVERAGE = 2,
- WPAINT_TOOL_SMEAR = 3,
-} eBrushWeightPaintTool;
-
-/* BrushGpencilSettings->brush type */
-typedef enum eBrushGPaintTool {
- GPAINT_TOOL_DRAW = 0,
- GPAINT_TOOL_FILL = 1,
- GPAINT_TOOL_ERASE = 2,
- GPAINT_TOOL_TINT = 3,
-} eBrushGPaintTool;
-
-/* BrushGpencilSettings->brush type */
-typedef enum eBrushGPVertexTool {
- GPVERTEX_TOOL_DRAW = 0,
- GPVERTEX_TOOL_BLUR = 1,
- GPVERTEX_TOOL_AVERAGE = 2,
- GPVERTEX_TOOL_TINT = 3,
- GPVERTEX_TOOL_SMEAR = 4,
- GPVERTEX_TOOL_REPLACE = 5,
-} eBrushGPVertexTool;
-
-/* BrushGpencilSettings->brush type */
-typedef enum eBrushGPSculptTool {
- GPSCULPT_TOOL_SMOOTH = 0,
- GPSCULPT_TOOL_THICKNESS = 1,
- GPSCULPT_TOOL_STRENGTH = 2,
- GPSCULPT_TOOL_GRAB = 3,
- GPSCULPT_TOOL_PUSH = 4,
- GPSCULPT_TOOL_TWIST = 5,
- GPSCULPT_TOOL_PINCH = 6,
- GPSCULPT_TOOL_RANDOMIZE = 7,
- GPSCULPT_TOOL_CLONE = 8,
-} eBrushGPSculptTool;
-
-/* BrushGpencilSettings->brush type */
-typedef enum eBrushGPWeightTool {
- GPWEIGHT_TOOL_DRAW = 0,
-} eBrushGPWeightTool;
-
-/* direction that the brush displaces along */
-enum {
- SCULPT_DISP_DIR_AREA = 0,
- SCULPT_DISP_DIR_VIEW = 1,
- SCULPT_DISP_DIR_X = 2,
- SCULPT_DISP_DIR_Y = 3,
- SCULPT_DISP_DIR_Z = 4,
-};
-
-typedef enum {
- BRUSH_MASK_DRAW = 0,
- BRUSH_MASK_SMOOTH = 1,
-} BrushMaskTool;
-
-/* blur kernel types, Brush.blur_mode */
-typedef enum eBlurKernelType {
- KERNEL_GAUSSIAN = 0,
- KERNEL_BOX = 1,
-} eBlurKernelType;
-
-/* Brush.falloff_shape */
-enum {
- PAINT_FALLOFF_SHAPE_SPHERE = 0,
- PAINT_FALLOFF_SHAPE_TUBE = 1,
-};
-
-#define MAX_BRUSH_PIXEL_RADIUS 500
-#define GP_MAX_BRUSH_PIXEL_RADIUS 1000
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index ae0bb20e529..832d55ea151 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -75,8 +75,7 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
- int typemap[50];
- char _pad[4];
+ int typemap[51];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -156,7 +155,9 @@ typedef enum CustomDataType {
CD_PROP_FLOAT3 = 48,
CD_PROP_FLOAT2 = 49,
- CD_NUMTYPES = 50,
+ CD_PROP_BOOL = 50,
+
+ CD_NUMTYPES = 51,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -208,6 +209,7 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR)
#define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3)
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
+#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index bd5afc457ac..9ac40495887 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -351,6 +351,7 @@ typedef enum eArrayGpencil_Flag {
GP_ARRAY_USE_OFFSET = (1 << 7),
GP_ARRAY_USE_RELATIVE = (1 << 8),
GP_ARRAY_USE_OB_OFFSET = (1 << 9),
+ GP_ARRAY_UNIFORM_RANDOM_SCALE = (1 << 10),
} eArrayGpencil_Flag;
typedef struct BuildGpencilModifierData {
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 212f0bfa1c9..949b0bb5bf5 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -32,8 +32,8 @@ extern "C" {
#endif
struct AnimData;
-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_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 6a91f4857b4..828c6ff2a51 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -40,8 +40,8 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_DIFFUSE_COLOR = (1 << 5),
EEVEE_RENDER_PASS_SPECULAR_LIGHT = (1 << 6),
EEVEE_RENDER_PASS_SPECULAR_COLOR = (1 << 7),
- EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE = (1 << 8),
- EEVEE_RENDER_PASS_VOLUME_SCATTER = (1 << 9),
+ EEVEE_RENDER_PASS_UNUSED_8 = (1 << 8),
+ EEVEE_RENDER_PASS_VOLUME_LIGHT = (1 << 9),
EEVEE_RENDER_PASS_EMIT = (1 << 10),
EEVEE_RENDER_PASS_ENVIRONMENT = (1 << 11),
EEVEE_RENDER_PASS_SHADOW = (1 << 12),
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 04fbc030ed9..c2337b28e54 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -35,7 +35,6 @@ struct AnimData;
struct BVHCache;
struct Ipo;
struct Key;
-struct LinkNode;
struct MCol;
struct MEdge;
struct MFace;
@@ -44,7 +43,6 @@ struct MLoopCol;
struct MLoopTri;
struct MLoopUV;
struct MPoly;
-struct MPropCol;
struct MVert;
struct Material;
struct Mesh;
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index e122d50cba8..fcb582ef837 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -503,8 +503,8 @@
.random_position = 0.0f, \
.rotation = 0.0f, \
.random_rotation = 0.0f, \
- .particle_offset = 1.0f, \
- .particle_amount = 0.0f, \
+ .particle_offset = 0.0f, \
+ .particle_amount = 1.0f, \
.index_layer_name = "", \
.value_layer_name = "", \
}
diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h
index 2b1fd546450..11cdb48edf0 100644
--- a/source/blender/makesdna/DNA_movieclip_types.h
+++ b/source/blender/makesdna/DNA_movieclip_types.h
@@ -24,8 +24,8 @@
#pragma once
#include "DNA_ID.h"
-#include "DNA_color_types.h" /* for color management */
-#include "DNA_tracking_types.h"
+#include "DNA_color_types.h" /* for color management */
+#include "DNA_tracking_types.h" /* for #MovieTracking */
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 13f8b11352a..7ad339c66af 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -25,15 +25,15 @@
#include "DNA_ID.h"
#include "DNA_listBase.h"
-#include "DNA_scene_types.h"
-#include "DNA_texture_types.h"
-#include "DNA_vec_types.h"
+#include "DNA_scene_types.h" /* for #ImageFormatData */
+#include "DNA_vec_types.h" /* for #rctf */
#ifdef __cplusplus
extern "C" {
#endif
struct AnimData;
+struct Collection;
struct ID;
struct Image;
struct ListBase;
@@ -160,6 +160,7 @@ typedef enum eNodeSocketDatatype {
SOCK_OBJECT = 8,
SOCK_IMAGE = 9,
SOCK_GEOMETRY = 10,
+ SOCK_COLLECTION = 11,
} eNodeSocketDatatype;
/* socket shape */
@@ -580,6 +581,10 @@ typedef struct bNodeSocketValueImage {
struct Image *value;
} bNodeSocketValueImage;
+typedef struct bNodeSocketValueCollection {
+ struct Collection *value;
+} bNodeSocketValueCollection;
+
/* data structs, for node->storage */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
@@ -1046,10 +1051,20 @@ typedef struct NodeSunBeams {
float ray_length;
} NodeSunBeams;
+typedef struct CryptomatteEntry {
+ struct CryptomatteEntry *next, *prev;
+ float encoded_hash;
+ /** MAX_NAME. */
+ char name[64];
+ char _pad[4];
+} CryptomatteEntry;
+
typedef struct NodeCryptomatte {
float add[3];
float remove[3];
- char *matte_id;
+ char *matte_id DNA_DEPRECATED;
+ /* Contains `CryptomatteEntry`. */
+ ListBase entries;
int num_inputs;
char _pad[4];
} NodeCryptomatte;
@@ -1059,6 +1074,20 @@ typedef struct NodeDenoise {
char _pad[7];
} NodeDenoise;
+typedef struct NodeAttributeMix {
+ /* e.g. MA_RAMP_BLEND. */
+ uint8_t blend_type;
+
+ /* GeometryNodeAttributeInputMode */
+ uint8_t input_type_factor;
+ uint8_t input_type_a;
+ uint8_t input_type_b;
+} NodeAttributeMix;
+
+typedef struct NodeAttributeColorRamp {
+ ColorBand color_ramp;
+} NodeAttributeColorRamp;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1464,6 +1493,11 @@ typedef enum GeometryNodeUseAttributeFlag {
GEO_NODE_USE_ATTRIBUTE_B = (1 << 1),
} GeometryNodeUseAttributeFlag;
+typedef enum GeometryNodePointInstanceType {
+ GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0,
+ GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1,
+} GeometryNodePointInstanceType;
+
typedef enum GeometryNodeAttributeInputMode {
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE = 0,
GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1,
@@ -1471,6 +1505,11 @@ typedef enum GeometryNodeAttributeInputMode {
GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3,
} GeometryNodeAttributeInputMode;
+typedef enum GeometryNodePointDistributeMethod {
+ GEO_NODE_POINT_DISTRIBUTE_RANDOM = 0,
+ GEO_NODE_POINT_DISTRIBUTE_POISSON = 1,
+} GeometryNodePointDistributeMethod;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 71f67d8a3b4..8b13db8a012 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -38,9 +38,8 @@ extern "C" {
struct AnimData;
struct BoundBox;
-struct DerivedMesh;
struct FluidsimSettings;
-struct GpencilBatchCache;
+struct GeometrySet;
struct Ipo;
struct Material;
struct Mesh;
@@ -51,7 +50,6 @@ struct RigidBodyOb;
struct SculptSession;
struct SoftBody;
struct bGPdata;
-struct GeometrySet;
/* Vertex Groups - Name Info */
typedef struct bDeformGroup {
@@ -150,14 +148,17 @@ typedef struct Object_Runtime {
*/
struct ID *data_orig;
/**
- * Object data structure created during object evaluation.
- * It has all modifiers applied.
+ * Object data structure created during object evaluation. It has all modifiers applied.
+ * The type is determined by the type of the original object. For example, for mesh and curve
+ * objects, this is a mesh. For a volume object, this is a volume.
*/
struct ID *data_eval;
/**
- * Some objects support evaluating to a geometry set instead of a single ID. In those cases the
- * evaluated geometry will be stored here instead of in #data_eval.
+ * Objects can evaluate to a geometry set instead of a single ID. In those cases, the evaluated
+ * geometry set will be stored here. An ID of the correct type is still stored in #data_eval.
+ * #geometry_set_eval might reference the ID pointed to by #data_eval as well, but does not own
+ * the data.
*/
struct GeometrySet *geometry_set_eval;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 85ec3dfdced..f73f99eb4e7 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -33,15 +33,10 @@
#define USE_SETSCENE_CHECK
#include "DNA_ID.h"
-#include "DNA_collection_types.h"
-#include "DNA_color_types.h" /* color management */
-#include "DNA_curveprofile_types.h"
+#include "DNA_color_types.h" /* color management */
#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */
-#include "DNA_freestyle_types.h"
#include "DNA_layer_types.h"
#include "DNA_listBase.h"
-#include "DNA_material_types.h"
-#include "DNA_userdef_types.h"
#include "DNA_vec_types.h"
#include "DNA_view3d_types.h"
@@ -323,8 +318,7 @@ typedef enum eScenePassType {
#define RE_PASSNAME_FREESTYLE "Freestyle"
#define RE_PASSNAME_BLOOM "BloomCol"
-#define RE_PASSNAME_VOLUME_TRANSMITTANCE "VolumeTransmCol"
-#define RE_PASSNAME_VOLUME_SCATTER "VolumeScatterCol"
+#define RE_PASSNAME_VOLUME_LIGHT "VolumeDir"
/* View - MultiView */
typedef struct SceneRenderView {
@@ -1345,6 +1339,18 @@ typedef struct MeshStatVis {
float sharp_min, sharp_max;
} MeshStatVis;
+typedef struct SequencerToolSettings {
+ /* eSeqImageFitMethod */
+ int fit_method;
+} SequencerToolSettings;
+
+typedef enum eSeqImageFitMethod {
+ SEQ_SCALE_TO_FIT,
+ SEQ_SCALE_TO_FILL,
+ SEQ_STRETCH_TO_FILL,
+ SEQ_USE_ORIGINAL_SIZE,
+} eSeqImageFitMethod;
+
/* *************************************************************** */
/* Tool Settings */
@@ -1519,6 +1525,9 @@ typedef struct ToolSettings {
* Temporary until there is a proper preset system that stores the profiles or maybe stores
* entire bevel configurations. */
struct CurveProfile *custom_bevel_profile_preset;
+
+ struct SequencerToolSettings *sequencer_tool_settings;
+
} ToolSettings;
/* *************************************************************** */
@@ -1783,7 +1792,7 @@ typedef struct Scene {
ListBase view_layers;
/* Not an actual datablock, but memory owned by scene. */
- Collection *master_collection;
+ struct Collection *master_collection;
struct SceneCollection *collection DNA_DEPRECATED;
/** Settings to be override by workspaces. */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index e21f3e1e706..59e5e9df9ee 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -33,9 +33,8 @@
#include "DNA_color_types.h"
#include "DNA_defs.h"
#include "DNA_listBase.h"
-#include "DNA_session_uuid_types.h"
-#include "DNA_vec_types.h"
-#include "DNA_vfont_types.h"
+#include "DNA_session_uuid_types.h" /* for #SessionUUID */
+#include "DNA_vec_types.h" /* for #rctf */
#ifdef __cplusplus
extern "C" {
@@ -44,6 +43,7 @@ extern "C" {
struct Ipo;
struct MovieClip;
struct Scene;
+struct VFont;
struct bSound;
/* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */
@@ -338,7 +338,7 @@ typedef struct GaussianBlurVars {
typedef struct TextVars {
char text[512];
- VFont *text_font;
+ struct VFont *text_font;
int text_blf_id;
int text_size;
float color[4], shadow_color[4], box_color[4];
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 6fd112628a1..4276e8b568e 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -621,6 +621,10 @@ typedef enum eSpaceSeq_Flag {
SEQ_SHOW_METADATA = (1 << 10),
SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */
SEQ_ZOOM_TO_FIT = (1 << 12),
+ SEQ_SHOW_STRIP_OVERLAY = (1 << 13),
+ SEQ_SHOW_STRIP_NAME = (1 << 14),
+ SEQ_SHOW_STRIP_SOURCE = (1 << 15),
+ SEQ_SHOW_STRIP_DURATION = (1 << 16),
} eSpaceSeq_Flag;
/* SpaceSeq.view */
@@ -664,6 +668,24 @@ 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;
+ 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. */
@@ -708,6 +730,7 @@ typedef struct FileSelectParams {
/** Details toggles (file size, creation date, etc.) */
char details_flags;
char _pad2[3];
+
/** Filter when (flags & FILE_FILTER) is true. */
int filter;
@@ -723,6 +746,32 @@ typedef struct FileSelectParams {
/* XXX --- end unused -- */
} FileSelectParams;
+/**
+ * File selection parameters for asset browsing mode, with #FileSelectParams as base.
+ */
+typedef struct FileAssetSelectParams {
+ FileSelectParams base_params;
+
+ FileSelectAssetLibraryUID asset_library;
+} FileAssetSelectParams;
+
+/**
+ * A wrapper to store previous and next folder lists (#FolderList) for a specific browse mode
+ * (#eFileBrowse_Mode).
+ */
+typedef struct FileFolderHistory {
+ struct FileFolderLists *next, *prev;
+
+ /** The browse mode this prev/next folder-lists are created for. */
+ char browse_mode; /* eFileBrowse_Mode */
+ char _pad[7];
+
+ /** Holds the list of previous directories to show. */
+ ListBase folders_prev;
+ /** Holds the list of next directories (pushed from previous) to show. */
+ ListBase folders_next;
+} FileFolderHistory;
+
/* File Browser */
typedef struct SpaceFile {
SpaceLink *next, *prev;
@@ -733,20 +782,42 @@ typedef struct SpaceFile {
char _pad0[6];
/* End 'SpaceLink' header. */
- char _pad1[4];
+ /** Is this a File Browser or an Asset Browser? */
+ char browse_mode; /* eFileBrowse_Mode */
+ char _pad1[1];
+
+ short tags;
+
int scroll_offset;
- /** Config and input for file select. */
- struct FileSelectParams *params;
+ /** Config and input for file select. One for each browse-mode, to keep them independent. */
+ FileSelectParams *params;
+ FileAssetSelectParams *asset_params;
- /** Holds the list of files to show. */
+ void *_pad2;
+
+ /**
+ * Holds the list of files to show.
+ * Currently recreated when browse-mode changes. Could be per browse-mode to avoid refreshes.
+ */
struct FileList *files;
- /** Holds the list of previous directories to show. */
+ /**
+ * Holds the list of previous directories to show. Owned by `folder_histories` below.
+ */
ListBase *folders_prev;
- /** Holds the list of next directories (pushed from previous) to show. */
+ /**
+ * Holds the list of next directories (pushed from previous) to show. Owned by
+ * `folder_histories` below.
+ */
ListBase *folders_next;
+ /**
+ * This actually owns the prev/next folder-lists above. On browse-mode change, the lists of the
+ * new mode get assigned to the above.
+ */
+ ListBase folder_histories; /* FileFolderHistory */
+
/* operator that is invoking fileselect
* op->exec() will be called on the 'Load' button.
* if operator provides op->cancel(), then this will be invoked
@@ -763,6 +834,30 @@ typedef struct SpaceFile {
short systemnr, system_bookmarknr;
} SpaceFile;
+/* SpaceFile.browse_mode (File Space Browsing Mode) */
+typedef enum eFileBrowse_Mode {
+ /* Regular Blender File Browser */
+ FILE_BROWSE_MODE_FILES = 0,
+ /* Asset Browser */
+ 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
@@ -792,6 +887,13 @@ enum eFileSortType {
FILE_SORT_SIZE = 4,
};
+/* SpaceFile.tags */
+enum eFileTags {
+ /** Tag the space as having to update files representing or containing main data. Must be set
+ * after file read and undo/redo. */
+ FILE_TAG_REBUILD_MAIN_FILES = (1 << 0),
+};
+
/* FileSelectParams.details_flags */
enum eFileDetails {
FILE_DETAILS_SIZE = (1 << 0),
@@ -810,6 +912,7 @@ enum eFileDetails {
typedef enum eFileSelectType {
FILE_LOADLIB = 1,
FILE_MAIN = 2,
+ FILE_MAIN_ASSET = 3,
FILE_UNIX = 8,
FILE_BLENDER = 8, /* don't display relative paths */
@@ -842,6 +945,7 @@ typedef enum eFileSel_Params_Flag {
FILE_SORT_INVERT = (1 << 11),
FILE_HIDE_TOOL_PROPS = (1 << 12),
FILE_CHECK_EXISTING = (1 << 13),
+ FILE_ASSETS_ONLY = (1 << 14),
} eFileSel_Params_Flag;
/* sfile->params->rename_flag */
@@ -885,6 +989,7 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_USD = (1 << 18),
FILE_TYPE_VOLUME = (1 << 19),
+ FILE_TYPE_ASSET = (1 << 28),
/** An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_DIR = (1 << 30),
FILE_TYPE_BLENDERLIB = (1u << 31),
@@ -985,9 +1090,16 @@ typedef struct FileDirEntry {
/** Optional argument for shortcuts, aliases etc. */
char *redirection_path;
- /** TODO: make this a real ID pointer? */
- void *poin;
- struct ImBuf *image;
+ /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), ID this file represents. Note comment
+ * for FileListInternEntry.local_data, the same applies here! */
+ ID *id;
+ /** If this file represents an asset, its asset data is here. Note that we may show assets of
+ * external files in which case this is set but not the id above.
+ * Note comment for FileListInternEntry.local_data, the same applies here! */
+ struct AssetMetaData *asset_data;
+
+ /* The icon_id for the preview image. */
+ int preview_icon_id;
/* Tags are for info only, most of filtering is done in asset engine. */
char **tags;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index f5ac6ca4496..1bd013c7d54 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -569,6 +569,13 @@ enum {
USER_MENU_TYPE_PROP = 4,
};
+typedef struct bUserAssetLibrary {
+ struct bUserAssetLibrary *next, *prev;
+
+ char name[64]; /* MAX_NAME */
+ char path[1024]; /* FILE_MAX */
+} bUserAssetLibrary;
+
typedef struct SolidLight {
int flag;
float smooth;
@@ -632,10 +639,12 @@ typedef struct UserDef_Experimental {
/* The following options are automatically sanitized (set to 0)
* when the release cycle is not alpha. */
char use_new_hair_type;
+ char use_new_point_cloud_type;
char use_sculpt_vertex_colors;
char use_switch_object_operator;
char use_sculpt_tools_tilt;
char use_object_add_tool;
+ char _pad[7];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
@@ -681,8 +690,7 @@ typedef struct UserDef {
short versions;
short dbl_click_time;
- char _pad0[2];
- char wheellinescroll;
+ char _pad0[3];
char mini_axis_type;
/** #eUserpref_UI_Flag. */
int uiflag;
@@ -739,6 +747,8 @@ typedef struct UserDef {
struct ListBase autoexec_paths;
/** #bUserMenu. */
struct ListBase user_menus;
+ /** #bUserAssetLibrary */
+ struct ListBase asset_libraries;
char keyconfigstr[64];
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index a7ad18d7cf3..f07af2c14a0 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -24,10 +24,8 @@
#pragma once
#include "DNA_listBase.h"
-#include "DNA_screen_types.h"
-#include "DNA_userdef_types.h"
-#include "DNA_vec_types.h"
-#include "DNA_xr_types.h"
+#include "DNA_screen_types.h" /* for #ScrAreaMap */
+#include "DNA_xr_types.h" /* for #XrSessionSettings */
#include "DNA_ID.h"
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index edca887639e..c5c2351c718 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -22,7 +22,7 @@
#pragma once
-#include "DNA_scene_types.h"
+#include "DNA_ID.h"
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index 2051335dd7e..a7adaa7e258 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -138,6 +138,7 @@ set(SRC
../../blenlib/intern/hash_mm2a.c
../../blenlib/intern/listbase.c
+ ../DNA_asset_defaults.h
../DNA_brush_defaults.h
../DNA_cachefile_defaults.h
../DNA_camera_defaults.h
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 1a8bd25215f..3e4d5d87fb0 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -85,6 +85,7 @@
#include "DNA_defaults.h"
#include "DNA_armature_types.h"
+#include "DNA_asset_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
@@ -117,6 +118,7 @@
#include "DNA_world_types.h"
#include "DNA_armature_defaults.h"
+#include "DNA_asset_defaults.h"
#include "DNA_brush_defaults.h"
#include "DNA_cachefile_defaults.h"
#include "DNA_camera_defaults.h"
@@ -148,6 +150,9 @@
#define SDNA_DEFAULT_DECL_STRUCT(struct_name) \
static const struct_name DNA_DEFAULT_##struct_name = _DNA_DEFAULT_##struct_name
+/* DNA_asset_defaults.h */
+SDNA_DEFAULT_DECL_STRUCT(AssetMetaData);
+
/* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(bArmature);
@@ -338,7 +343,10 @@ extern const bTheme U_theme_default;
/** Keep headers sorted. */
const void *DNA_default_table[SDNA_TYPE_MAX] = {
- /* DNA_arnature_defaults.h */
+ /* DNA_asset_defaults.h */
+ SDNA_DEFAULT_DECL(AssetMetaData),
+
+ /* DNA_armature_defaults.h */
SDNA_DEFAULT_DECL(bArmature),
/* DNA_brush_defaults.h */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 81a7da7b4d8..54d2bc88d16 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -139,6 +139,7 @@ static const char *includefiles[] = {
"DNA_volume_types.h",
"DNA_simulation_types.h",
"DNA_pointcache_types.h",
+ "DNA_asset_types.h",
/* see comment above before editing! */
@@ -1533,6 +1534,7 @@ int main(int argc, char **argv)
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
+#include "DNA_asset_types.h"
#include "DNA_boid_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index a581edcb04b..aaa948dbff6 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -70,6 +70,8 @@ extern StructRNA RNA_ArrayGpencilModifier;
extern StructRNA RNA_ArrayModifier;
extern StructRNA RNA_Attribute;
extern StructRNA RNA_AttributeGroup;
+extern StructRNA RNA_AssetMetaData;
+extern StructRNA RNA_AssetTag;
extern StructRNA RNA_BackgroundImage;
extern StructRNA RNA_BevelModifier;
extern StructRNA RNA_BezierSplinePoint;
@@ -202,6 +204,7 @@ extern StructRNA RNA_CopyRotationConstraint;
extern StructRNA RNA_CopyScaleConstraint;
extern StructRNA RNA_CopyTransformsConstraint;
extern StructRNA RNA_CorrectiveSmoothModifier;
+extern StructRNA RNA_CryptomatteEntry;
extern StructRNA RNA_Curve;
extern StructRNA RNA_CurveMap;
extern StructRNA RNA_CurveMapPoint;
@@ -250,7 +253,9 @@ extern StructRNA RNA_FModifierPython;
extern StructRNA RNA_FModifierStepped;
extern StructRNA RNA_FaceMap;
extern StructRNA RNA_FieldSettings;
+extern StructRNA RNA_FileAssetSelectParams;
extern StructRNA RNA_FileBrowserFSMenuEntry;
+extern StructRNA RNA_FileSelectEntry;
extern StructRNA RNA_FileSelectParams;
extern StructRNA RNA_FloatAttribute;
extern StructRNA RNA_FloatAttributeValue;
@@ -694,6 +699,7 @@ extern StructRNA RNA_UVProjector;
extern StructRNA RNA_UVWarpModifier;
extern StructRNA RNA_UnitSettings;
extern StructRNA RNA_UnknownType;
+extern StructRNA RNA_UserAssetLibrary;
extern StructRNA RNA_UserSolidLight;
extern StructRNA RNA_VertexcolorGpencilModifier;
extern StructRNA RNA_VectorFont;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index a94466e30c2..69bd6142cad 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -56,6 +56,7 @@ extern const EnumPropertyItem rna_enum_mesh_select_mode_items[];
extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[];
extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[];
extern const EnumPropertyItem rna_enum_space_graph_mode_items[];
+extern const EnumPropertyItem rna_enum_space_file_browse_mode_items[];
extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[];
extern const EnumPropertyItem rna_enum_space_type_items[];
extern const EnumPropertyItem rna_enum_space_image_mode_items[];
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 9aea5b26a54..3db8909c8a7 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -31,6 +31,7 @@ set(DEFSRC
rna_animviz.c
rna_armature.c
rna_attribute.c
+ rna_asset.c
rna_boid.c
rna_brush.c
rna_cachefile.c
@@ -99,6 +100,7 @@ set(DEFSRC
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
list(APPEND DEFSRC
rna_hair.c
@@ -426,6 +428,7 @@ set(LIB
bf_editor_animation
bf_editor_armature
+ bf_editor_asset
bf_editor_curve
bf_editor_gizmo_library
bf_editor_gpencil
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 9eed1fcf085..c2c95c59002 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4270,6 +4270,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_animviz.c", NULL, RNA_def_animviz},
{"rna_armature.c", "rna_armature_api.c", RNA_def_armature},
{"rna_attribute.c", NULL, RNA_def_attribute},
+ {"rna_asset.c", NULL, RNA_def_asset},
{"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
{"rna_cachefile.c", NULL, RNA_def_cachefile},
@@ -4308,7 +4309,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
+#ifdef WITH_POINT_CLOUD
{"rna_pointcloud.c", NULL, RNA_def_pointcloud},
+#endif
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index d0e0b69a8d5..7c67bfc360b 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -289,9 +289,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_PaintCurve) {
return ID_PC;
}
+# ifdef WITH_POINT_CLOUD
if (base_type == &RNA_PointCloud) {
return ID_PT;
}
+# endif
if (base_type == &RNA_LightProbe) {
return ID_LP;
}
@@ -397,7 +399,11 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_PC:
return &RNA_PaintCurve;
case ID_PT:
+# ifdef WITH_POINT_CLOUD
return &RNA_PointCloud;
+# else
+ return &RNA_ID;
+# endif
case ID_LP:
return &RNA_LightProbe;
case ID_SCE:
@@ -1522,6 +1528,11 @@ static void rna_def_ID(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from");
+ prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+ RNA_def_property_ui_text(prop, "Asset Data", "Additional data for an asset data-block");
+
prop = RNA_def_pointer(
srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 6586cfc7969..4262d5590c8 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1105,8 +1105,11 @@ bool RNA_struct_bl_idname_ok_or_report(ReportList *reports,
const bool failure = false;
#endif
if (p == NULL || p == identifier || p + len_sep >= identifier + len_id) {
- BKE_reportf(
- reports, report_level, "'%s' doesn't contain '%s' with prefix & suffix", identifier, sep);
+ BKE_reportf(reports,
+ report_level,
+ "'%s' does not contain '%s' with prefix and suffix",
+ identifier,
+ sep);
return failure;
}
@@ -5805,7 +5808,7 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
if (r_path) {
*r_path = "collection";
}
- return (ID *)BKE_collection_master_scene_search(bmain, (Collection *)id);
+ return (ID *)BKE_collection_master_scene_search(bmain, (struct Collection *)id);
default:
return NULL;
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
new file mode 100644
index 00000000000..1af53e95cc9
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -0,0 +1,228 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "DNA_asset_types.h"
+#include "DNA_defs.h"
+
+#include "rna_internal.h"
+
+#ifdef RNA_RUNTIME
+
+# include "BKE_asset.h"
+# include "BKE_idprop.h"
+
+# include "BLI_listbase.h"
+
+# include "RNA_access.h"
+
+static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data,
+ ReportList *reports,
+ const char *name,
+ bool skip_if_exists)
+{
+ AssetTag *tag = NULL;
+
+ if (skip_if_exists) {
+ struct AssetTagEnsureResult result = BKE_asset_metadata_tag_ensure(asset_data, name);
+
+ if (!result.is_new) {
+ BKE_reportf(
+ reports, RPT_WARNING, "Tag '%s' already present for given asset", result.tag->name);
+ /* Report, but still return valid item. */
+ }
+ tag = result.tag;
+ }
+ else {
+ tag = BKE_asset_metadata_tag_add(asset_data, name);
+ }
+
+ return tag;
+}
+
+static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data,
+ ReportList *reports,
+ PointerRNA *tag_ptr)
+{
+ AssetTag *tag = tag_ptr->data;
+ if (BLI_findindex(&asset_data->tags, tag) == -1) {
+ BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name);
+ return;
+ }
+
+ BKE_asset_metadata_tag_remove(asset_data, tag);
+ RNA_POINTER_INVALIDATE(tag_ptr);
+}
+
+static IDProperty *rna_AssetMetaData_idprops(PointerRNA *ptr, bool create)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ if (create && !asset_data->properties) {
+ IDPropertyTemplate val = {0};
+ asset_data->properties = IDP_New(IDP_GROUP, &val, "RNA_AssetMetaData group");
+ }
+
+ return asset_data->properties;
+}
+
+static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ if (asset_data->description) {
+ strcpy(value, asset_data->description);
+ }
+ else {
+ value[0] = '\0';
+ }
+}
+
+static int rna_AssetMetaData_description_length(PointerRNA *ptr)
+{
+ AssetMetaData *asset_data = ptr->data;
+ return asset_data->description ? strlen(asset_data->description) : 0;
+}
+
+static void rna_AssetMetaData_description_set(PointerRNA *ptr, const char *value)
+{
+ AssetMetaData *asset_data = ptr->data;
+
+ if (asset_data->description) {
+ MEM_freeN(asset_data->description);
+ }
+
+ if (value[0]) {
+ asset_data->description = BLI_strdup(value);
+ }
+ else {
+ asset_data->description = NULL;
+ }
+}
+
+static void rna_AssetMetaData_active_tag_range(
+ PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
+{
+ const AssetMetaData *asset_data = ptr->data;
+ *min = *softmin = 0;
+ *max = *softmax = MAX2(asset_data->tot_tags - 1, 0);
+}
+
+#else
+
+static void rna_def_asset_tag(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "AssetTag", NULL);
+ RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, MAX_NAME);
+ RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag");
+ RNA_def_struct_name_property(srna, prop);
+}
+
+static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "AssetTags");
+ srna = RNA_def_struct(brna, "AssetTags", NULL);
+ RNA_def_struct_sdna(srna, "AssetMetaData");
+ RNA_def_struct_ui_text(srna, "Asset Tags", "Collection of custom asset tags");
+
+ /* Tag collection */
+ func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new");
+ RNA_def_function_ui_description(func, "Add a new tag to this asset");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_boolean(func,
+ "skip_if_exists",
+ false,
+ "Skip if Exists",
+ "Do not add a new tag if one of the same type already exists");
+ /* return type */
+ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "New tag");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove");
+ RNA_def_function_ui_description(func, "Remove an existing tag from this asset");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ /* tag to remove */
+ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+}
+
+static void rna_def_asset_data(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ 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!. */
+ /* 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! */
+
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop,
+ "rna_AssetMetaData_description_get",
+ "rna_AssetMetaData_description_length",
+ "rna_AssetMetaData_description_set");
+ RNA_def_property_ui_text(
+ prop, "Description", "A description of the asset to be displayed for the user");
+
+ prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "AssetTag");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Tags",
+ "Custom tags (name tokens) for the asset, used for filtering and "
+ "general asset management");
+ rna_def_asset_tags_api(brna, prop);
+
+ prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE);
+ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range");
+ RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing");
+}
+
+void RNA_def_asset(BlenderRNA *brna)
+{
+ RNA_define_animate_sdna(false);
+
+ rna_def_asset_tag(brna);
+ rna_def_asset_data(brna);
+
+ RNA_define_animate_sdna(true);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 95f6340174a..f98ca47d767 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -44,6 +44,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{CD_PROP_COLOR, "FLOAT_COLOR", 0, "Float Color", "RGBA color with floating-point precisions"},
{CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"},
{CD_PROP_STRING, "STRING", 0, "String", "Text string"},
+ {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index b5ce7976fd8..a361feba439 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -22,6 +22,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
@@ -133,6 +134,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""},
{SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""},
{SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""},
+ {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""},
{SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""},
{SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""},
{SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""},
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 1810cee5cee..df9f4674900 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -662,7 +662,7 @@ void RNA_def_camera(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOW_SAFE_CENTER);
RNA_def_property_ui_text(prop,
- "Show Center-cut safe areas",
+ "Show Center-Cut Safe Areas",
"Show safe areas to fit content in a different aspect ratio");
RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL);
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 0b3dc2a3504..4f5828311d8 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -856,7 +856,7 @@ static void rna_def_constraint_headtail_common(StructRNA *srna)
prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, "bConstraint", "headtail");
- RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1");
+ RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head is 0, Tail is 1");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "use_bbone_shape", PROP_BOOLEAN, PROP_NONE);
@@ -1051,7 +1051,7 @@ static void rna_def_constraint_python(BlenderRNA *brna)
prop = RNA_def_property(srna, "target_count", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "tarnum");
- RNA_def_property_ui_text(prop, "Number of Targets", "Usually only 1-3 are needed");
+ RNA_def_property_ui_text(prop, "Number of Targets", "Usually only 1 to 3 are needed");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update");
prop = RNA_def_property(srna, "text", PROP_POINTER, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 8734984d4e1..475b4d33936 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -2341,7 +2341,7 @@ static void rna_def_fcurve(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION, "rna_FCurve_update_data_relations");
/* called 'index' when given as function arg */
- prop = RNA_def_property(srna, "array_index", PROP_INT, PROP_NONE);
+ prop = RNA_def_property(srna, "array_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_ui_text(
prop, "RNA Array Index", "Index to the specific property affected by F-Curve if applicable");
/* XXX need an update callback for this so that animation gets evaluated */
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index 90f5434bea0..bb8280ede91 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -1552,7 +1552,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_dynamic_array_funcs(prop, "rna_FluidModifier_grid_get_length");
RNA_def_property_float_funcs(prop, "rna_FluidModifier_temperature_grid_get", NULL, NULL);
RNA_def_property_ui_text(
- prop, "Temperature Grid", "Smoke temperature grid, range 0..1 represents 0..1000K");
+ prop, "Temperature Grid", "Smoke temperature grid, range 0 to 1 represents 0 to 1000K");
# endif /* WITH_FLUID */
/* domain object data */
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 7be9d14b1d1..72e11838fac 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -403,13 +403,13 @@ static char *rna_GPencilLayerMask_path(PointerRNA *ptr)
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
bGPDlayer_Mask *mask = (bGPDlayer_Mask *)ptr->data;
- char name_layer[sizeof(gpl->info) * 2];
- char name_mask[sizeof(mask->name) * 2];
+ char gpl_info_esc[sizeof(gpl->info) * 2];
+ char mask_name_esc[sizeof(mask->name) * 2];
- BLI_str_escape(name_layer, gpl->info, sizeof(name_layer));
- BLI_str_escape(name_mask, mask->name, sizeof(name_mask));
+ BLI_str_escape(gpl_info_esc, gpl->info, sizeof(gpl_info_esc));
+ BLI_str_escape(mask_name_esc, mask->name, sizeof(mask_name_esc));
- return BLI_sprintfN("layers[\"%s\"].mask_layers[\"%s\"]", name_layer, name_mask);
+ return BLI_sprintfN("layers[\"%s\"].mask_layers[\"%s\"]", gpl_info_esc, mask_name_esc);
}
static int rna_GPencil_active_mask_index_get(PointerRNA *ptr)
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 5f131de6a40..89eb989a442 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -1560,6 +1560,12 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_RELATIVE);
RNA_def_property_ui_text(prop, "Shift", "Enable shift");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_UNIFORM_RANDOM_SCALE);
+ RNA_def_property_ui_text(
+ prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
}
static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 1c6f83efd65..76c3e17e128 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -152,6 +152,7 @@ void RNA_def_animation(struct BlenderRNA *brna);
void RNA_def_animviz(struct BlenderRNA *brna);
void RNA_def_armature(struct BlenderRNA *brna);
void RNA_def_attribute(struct BlenderRNA *brna);
+void RNA_def_asset(struct BlenderRNA *brna);
void RNA_def_boid(struct BlenderRNA *brna);
void RNA_def_brush(struct BlenderRNA *brna);
void RNA_def_cachefile(struct BlenderRNA *brna);
@@ -470,7 +471,9 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop);
#ifdef WITH_HAIR_NODES
void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
#endif
+#ifdef WITH_POINT_CLOUD
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
+#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
#ifdef WITH_GEOMETRY_NODES
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index e43079c967f..433d499b4c1 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -127,7 +127,7 @@ static void rna_def_light(BlenderRNA *brna)
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_light_type_items);
- RNA_def_property_ui_text(prop, "Type", "Type of Light");
+ RNA_def_property_ui_text(prop, "Type", "Type of light");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_LIGHT);
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 0f17f8c44cd..aa22a4307d2 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -128,7 +128,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects)
RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves)
RNA_MAIN_LISTBASE_FUNCS_DEF(palettes)
RNA_MAIN_LISTBASE_FUNCS_DEF(particles)
+# ifdef WITH_POINT_CLOUD
RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
@@ -391,12 +393,14 @@ void RNA_def_main(BlenderRNA *brna)
# ifdef WITH_HAIR_NODES
{"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs},
# endif
+# ifdef WITH_POINT_CLOUD
{"pointclouds",
"PointCloud",
"rna_Main_pointclouds_begin",
"Point Clouds",
"Point cloud data-blocks",
RNA_def_main_pointclouds},
+# endif
{"volumes",
"Volume",
"rna_Main_volumes_begin",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 5fc2cce9bc6..21ff44ed253 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -678,6 +678,7 @@ static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
}
# endif
+# ifdef WITH_POINT_CLOUD
static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -687,6 +688,7 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
id_us_min(&pointcloud->id);
return pointcloud;
}
+# endif
static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
{
@@ -755,7 +757,9 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
# ifdef WITH_HAIR_NODES
RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
# endif
+# ifdef WITH_POINT_CLOUD
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
+# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
# ifdef WITH_GEOMETRY_NODES
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
@@ -2206,6 +2210,7 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop)
}
# endif
+# ifdef WITH_POINT_CLOUD
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2252,6 +2257,7 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+# endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
{
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 086a182e085..94b56e4f4e0 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -683,7 +683,7 @@ void RNA_def_material(BlenderRNA *brna)
{MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"},
{MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"},
{MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"},
- {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader Ball"},
+ {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader ball"},
{MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"},
{MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index d91b6ea19f7..c32cc89aa30 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -1603,12 +1603,10 @@ static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr)
# endif
}
-static bool rna_NodesModifier_node_group_poll(PointerRNA *ptr, PointerRNA value)
+static bool rna_NodesModifier_node_group_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
{
- NodesModifierData *nmd = ptr->data;
bNodeTree *ntree = value.data;
- UNUSED_VARS(nmd, ntree);
- return true;
+ return ntree->type == NTREE_GEOMETRY;
}
static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -4884,13 +4882,13 @@ static void rna_def_modifier_screw(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_stretch_u", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SCREW_UV_STRETCH_U);
RNA_def_property_ui_text(
- prop, "Stretch U", "Stretch the U coordinates between 0-1 when UV's are present");
+ prop, "Stretch U", "Stretch the U coordinates between 0 and 1 when UV's are present");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_stretch_v", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SCREW_UV_STRETCH_V);
RNA_def_property_ui_text(
- prop, "Stretch V", "Stretch the V coordinates between 0-1 when UV's are present");
+ prop, "Stretch V", "Stretch the V coordinates between 0 and 1 when UV's are present");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
# if 0
@@ -5135,7 +5133,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Normalize Weights",
- "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)");
+ "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "map_curve", PROP_POINTER, PROP_NONE);
@@ -5300,7 +5298,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Normalize Weights",
- "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)");
+ "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
@@ -5424,7 +5422,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Normalize Weights",
- "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)");
+ "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "map_curve", PROP_POINTER, PROP_NONE);
@@ -5973,7 +5971,7 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna)
"FACTOR",
0,
"Factor",
- "Control playback using a value between [0, 1]"},
+ "Control playback using a value between 0 and 1"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c
index f8a342e7f7e..2642ba82bc0 100644
--- a/source/blender/makesrna/intern/rna_nla.c
+++ b/source/blender/makesrna/intern/rna_nla.c
@@ -784,7 +784,7 @@ static void rna_def_nlastrip(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_animated_time_cyclic", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME_CYCLIC);
RNA_def_property_ui_text(
- prop, "Cyclic Strip Time", "Cycle the animated time within the action start & end");
+ prop, "Cyclic Strip Time", "Cycle the animated time within the action start and end");
RNA_def_property_update(
prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update");
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index dd02cc214e0..d4ac3d1b084 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -37,6 +37,7 @@
#include "BKE_animsys.h"
#include "BKE_attribute.h"
+#include "BKE_cryptomatte.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_texture.h"
@@ -85,6 +86,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
+ {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -98,6 +100,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{SOCK_OBJECT, "OBJECT", 0, "Object", ""},
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
+ {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -272,32 +275,32 @@ const EnumPropertyItem rna_enum_node_float_compare_items[] = {
{NODE_FLOAT_COMPARE_LESS_THAN,
"LESS_THAN",
0,
- "A < B",
+ "Less Than",
"True when the first input is smaller than second input"},
{NODE_FLOAT_COMPARE_LESS_EQUAL,
"LESS_EQUAL",
0,
- "A <= B",
+ "Less Than or Equal",
"True when the first input is smaller than the second input or equal"},
{NODE_FLOAT_COMPARE_GREATER_THAN,
"GREATER_THAN",
0,
- "A > B",
+ "Greater Than",
"True when the first input is greater than the second input"},
{NODE_FLOAT_COMPARE_GREATER_EQUAL,
"GREATER_EQUAL",
0,
- "A >= B",
+ "Greater Than or Equal",
"True when the first input is greater than the second input or equal"},
{NODE_FLOAT_COMPARE_EQUAL,
"EQUAL",
0,
- "A = B",
+ "Equal",
"True when both inputs are approximately equal"},
{NODE_FLOAT_COMPARE_NOT_EQUAL,
"NOT_EQUAL",
0,
- "A != B",
+ "Not Equal",
"True when both inputs are not approximately equal"},
{0, NULL, 0, NULL, NULL},
};
@@ -435,6 +438,34 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_node_geometry_attribute_factor_input_type_items[] = {
+ {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""},
+ {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items[] = {
+ {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""},
+ {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""},
+ {GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", ""},
+ {GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
+ {GEO_NODE_POINT_DISTRIBUTE_RANDOM,
+ "RANDOM",
+ 0,
+ "Random",
+ "Distribute points randomly on the surface"},
+ {GEO_NODE_POINT_DISTRIBUTE_POISSON,
+ "POISSON",
+ 0,
+ "Poisson Disk",
+ "Project points on the surface evenly with a Poisson disk distribution"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#endif
#ifdef RNA_RUNTIME
@@ -1859,7 +1890,7 @@ static const EnumPropertyItem *itemf_function_check(
static bool attribute_random_type_supported(const EnumPropertyItem *item)
{
- return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL);
}
static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
@@ -1879,6 +1910,30 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf(
return itemf_function_check(rna_enum_attribute_domain_items, attribute_random_domain_supported);
}
+static bool attribute_fill_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported);
+}
+
+static bool attribute_fill_domain_supported(const EnumPropertyItem *item)
+{
+ return item->value == ATTR_DOMAIN_POINT;
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeFill_domain_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_domain_items, attribute_fill_domain_supported);
+}
+
static bool attribute_math_operation_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
@@ -3586,33 +3641,26 @@ static void rna_NodeCryptomatte_matte_get(PointerRNA *ptr, char *value)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
-
- strcpy(value, (nc->matte_id) ? nc->matte_id : "");
+ char *matte_id = BKE_cryptomatte_entries_to_matte_id(nc);
+ strcpy(value, matte_id);
+ MEM_freeN(matte_id);
}
static int rna_NodeCryptomatte_matte_length(PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
-
- return (nc->matte_id) ? strlen(nc->matte_id) : 0;
+ char *matte_id = BKE_cryptomatte_entries_to_matte_id(nc);
+ int result = strlen(matte_id);
+ MEM_freeN(matte_id);
+ return result;
}
static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value)
{
bNode *node = (bNode *)ptr->data;
NodeCryptomatte *nc = node->storage;
-
- if (nc->matte_id) {
- MEM_freeN(nc->matte_id);
- }
-
- if (value && value[0]) {
- nc->matte_id = BLI_strdup(value);
- }
- else {
- nc->matte_id = NULL;
- }
+ BKE_cryptomatte_matte_id_to_entries(NULL, nc, value);
}
static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -4268,7 +4316,7 @@ static void def_math(StructRNA *srna)
prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom2", SHD_MATH_CLAMP);
- RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range");
+ RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@@ -4387,7 +4435,7 @@ static void def_mix_rgb(StructRNA *srna)
prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom2", SHD_MIXRGB_CLAMP);
- RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range");
+ RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@@ -5139,12 +5187,12 @@ static void def_sh_tex_pointdensity(StructRNA *srna)
"PARTICLE_AGE",
0,
"Particle Age",
- "Lifetime mapped as 0.0 - 1.0 intensity"},
+ "Lifetime mapped as 0.0 to 1.0 intensity"},
{SHD_POINTDENSITY_COLOR_PARTSPEED,
"PARTICLE_SPEED",
0,
"Particle Speed",
- "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"},
+ "Particle speed (absolute magnitude of velocity) mapped as 0.0 to 1.0 intensity"},
{SHD_POINTDENSITY_COLOR_PARTVEL,
"PARTICLE_VELOCITY",
0,
@@ -5899,7 +5947,7 @@ static void def_cmp_map_range(StructRNA *srna)
prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1);
- RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range");
+ RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
@@ -6799,8 +6847,8 @@ static void def_cmp_defocus(StructRNA *srna)
RNA_def_property_range(prop, 0.0f, 128.0f);
RNA_def_property_ui_text(
prop,
- "F-stop",
- "Amount of focal blur, 128=infinity=perfect focus, half the value doubles "
+ "F-Stop",
+ "Amount of focal blur, 128 (infinity) is perfect focus, half the value doubles "
"the blur radius");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -6816,7 +6864,7 @@ static void def_cmp_defocus(StructRNA *srna)
RNA_def_property_ui_text(
prop,
"Threshold",
- "CoC radius threshold, prevents background bleed on in-focus midground, 0=off");
+ "CoC radius threshold, prevents background bleed on in-focus midground, 0 is disabled");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "use_preview", PROP_BOOLEAN, PROP_NONE);
@@ -7602,8 +7650,8 @@ static void def_cmp_bokehblur(StructRNA *srna)
RNA_def_property_range(prop, 0.0f, 128.0f);
RNA_def_property_ui_text(
prop,
- "F-stop",
- "Amount of focal blur, 128=infinity=perfect focus, half the value doubles "
+ "F-Stop",
+ "Amount of focal blur, 128 (infinity) is perfect focus, half the value doubles "
"the blur radius");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
# endif
@@ -8154,7 +8202,7 @@ static void def_cmp_sunbeams(StructRNA *srna)
RNA_def_property_range(prop, -100.0f, 100.0f);
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 10, 3);
RNA_def_property_ui_text(
- prop, "Source", "Source point of rays as a factor of the image width & height");
+ prop, "Source", "Source point of rays as a factor of the image width and height");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "ray_length", PROP_FLOAT, PROP_UNSIGNED);
@@ -8165,6 +8213,24 @@ static void def_cmp_sunbeams(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_cmp_cryptomatte_entry(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CryptomatteEntry", NULL);
+ RNA_def_struct_sdna(srna, "CryptomatteEntry");
+
+ prop = RNA_def_property(srna, "encoded_hash", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_float_sdna(prop, NULL, "encoded_hash");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Name", "");
+ RNA_def_struct_name_property(srna, prop);
+}
+
static void def_cmp_cryptomatte(StructRNA *srna)
{
PropertyRNA *prop;
@@ -8336,13 +8402,20 @@ static void def_geo_attribute_create_common(StructRNA *srna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
-static void def_geo_random_attribute(StructRNA *srna)
+static void def_geo_attribute_randomize(StructRNA *srna)
{
def_geo_attribute_create_common(srna,
"rna_GeometryNodeAttributeRandom_type_itemf",
"rna_GeometryNodeAttributeRandom_domain_itemf");
}
+static void def_geo_attribute_fill(StructRNA *srna)
+{
+ def_geo_attribute_create_common(srna,
+ "rna_GeometryNodeAttributeFill_type_itemf",
+ "rna_GeometryNodeAttributeFill_domain_itemf");
+}
+
static void def_geo_attribute_math(StructRNA *srna)
{
PropertyRNA *prop;
@@ -8368,6 +8441,84 @@ static void def_geo_attribute_math(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_point_instance(StructRNA *srna)
+{
+ static const EnumPropertyItem instance_type_items[] = {
+ {GEO_NODE_POINT_INSTANCE_TYPE_OBJECT,
+ "OBJECT",
+ ICON_NONE,
+ "Object",
+ "Instance an individual object on all points"},
+ {GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION,
+ "COLLECTION",
+ ICON_NONE,
+ "Collection",
+ "Instance an entire collection on all points"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, instance_type_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
+ RNA_def_property_ui_text(prop, "Instance Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_attribute_mix(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeMix", "storage");
+
+ prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_ramp_blend_items);
+ RNA_def_property_enum_default(prop, MA_RAMP_BLEND);
+ RNA_def_property_ui_text(prop, "Blending Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_factor_input_type_items);
+ RNA_def_property_ui_text(prop, "Input Type Factor", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items);
+ RNA_def_property_ui_text(prop, "Input Type A", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items);
+ RNA_def_property_ui_text(prop, "Input Type B", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_point_distribute(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_point_distribute_method_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM);
+ RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_attribute_color_ramp(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeColorRamp", "storage");
+
+ prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ColorRamp");
+ RNA_def_property_ui_text(prop, "Color Ramp", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -8393,6 +8544,8 @@ static void rna_def_compositor_node(BlenderRNA *brna)
/* compositor node need_exec flag */
func = RNA_def_function(srna, "tag_need_exec", "rna_CompositorNode_tag_need_exec");
RNA_def_function_ui_description(func, "Tag the node for compositor update");
+
+ def_cmp_cryptomatte_entry(brna);
}
static void rna_def_texture_node(BlenderRNA *brna)
@@ -9074,6 +9227,41 @@ static void rna_def_node_socket_geometry(BlenderRNA *brna,
RNA_def_struct_sdna(srna, "bNodeSocket");
}
+static void rna_def_node_socket_collection(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Collection");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+
+ /* socket interface */
+ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
+ RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Collection");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+}
+
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@@ -9214,6 +9402,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage");
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
+
+ rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection");
}
static void rna_def_internal_node(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index ee115b74379..3e8d8e10b37 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -579,7 +579,11 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
return &RNA_ID;
# endif
case OB_POINTCLOUD:
+# ifdef WITH_POINT_CLOUD
return &RNA_PointCloud;
+# else
+ return &RNA_ID;
+# endif
case OB_VOLUME:
return &RNA_Volume;
default:
@@ -2751,8 +2755,8 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Track Axis",
- "Axis that points in 'forward' direction (applies to InstanceFrame when "
- "parent 'Follow' is enabled)");
+ "Axis that points in the 'forward' direction (applies to Instance Vertices when "
+ "Align to Vertex Normal is enabled)");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update");
prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE);
@@ -2761,8 +2765,8 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Up Axis",
- "Axis that points in the upward direction (applies to InstanceFrame when "
- "parent 'Follow' is enabled)");
+ "Axis that points in the upward direction (applies to Instance Vertices when "
+ "Align to Vertex Normal is enabled)");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update");
/* proxy */
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index db4f3754b25..12fd2b78d91 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include "DNA_cloth_types.h"
+#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
@@ -1486,10 +1487,11 @@ static void rna_def_field(BlenderRNA *brna)
prop = RNA_def_property(srna, "texture_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "tex_mode");
RNA_def_property_enum_items(prop, texture_items);
- RNA_def_property_ui_text(prop,
- "Texture Mode",
- "How the texture effect is calculated (RGB & Curl need a RGB texture, "
- "else Gradient will be used instead)");
+ RNA_def_property_ui_text(
+ prop,
+ "Texture Mode",
+ "How the texture effect is calculated (RGB and Curl need a RGB texture, "
+ "else Gradient will be used instead)");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
prop = RNA_def_property(srna, "z_direction", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index e04ef105071..5fa93ec3f04 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -2948,7 +2948,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop,
"Adaptive Subframe Threshold",
"The relative distance a particle can move before requiring more subframes "
- "(target Courant number); 0.01-0.3 is the recommended range");
+ "(target Courant number); 0.01 to 0.3 is the recommended range");
RNA_def_property_update(prop, 0, "rna_Particle_reset");
prop = RNA_def_property(srna, "jitter_factor", PROP_FLOAT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index 5f74e8cfc78..aad94f4729f 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -1383,7 +1383,7 @@ static void rna_def_pose_channel(BlenderRNA *brna)
"rna_PoseChannel_bone_group_index_set",
"rna_PoseChannel_bone_group_index_range");
RNA_def_property_ui_text(
- prop, "Bone Group Index", "Bone Group this pose channel belongs to (0=no group)");
+ prop, "Bone Group Index", "Bone group this pose channel belongs to (0 means no group)");
RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
@@ -1551,14 +1551,13 @@ static void rna_def_pose_itasc(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Feedback",
- "Feedback coefficient for error correction, average response time is 1/feedback "
- "(default=20)");
+ "Feedback coefficient for error correction, average response time is 1/feedback");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
prop = RNA_def_property(srna, "velocity_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "maxvel");
RNA_def_property_range(prop, 0.0f, 100.0f);
- RNA_def_property_ui_text(prop, "Max Velocity", "Maximum joint velocity in rad/s (default=50)");
+ RNA_def_property_ui_text(prop, "Max Velocity", "Maximum joint velocity in radians/second");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE);
@@ -1574,7 +1573,7 @@ static void rna_def_pose_itasc(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Damp",
"Maximum damping coefficient when singular value is nearly 0 "
- "(higher values=more stability, less reactivity - default=0.5)");
+ "(higher values produce results with more stability, less reactivity)");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
prop = RNA_def_property(srna, "damping_epsilon", PROP_FLOAT, PROP_FACTOR);
@@ -1583,7 +1582,7 @@ static void rna_def_pose_itasc(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Epsilon",
"Singular value under which damping is progressively applied "
- "(higher values=more stability, less reactivity - default=0.1)");
+ "(higher values produce results with more stability, less reactivity)");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update");
}
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 98ae7591062..e2001eacf4c 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -3076,7 +3076,7 @@ static void rna_def_string_property(StructRNA *srna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_string_funcs(
prop, "rna_StringProperty_default_get", "rna_StringProperty_default_length", NULL);
- RNA_def_property_ui_text(prop, "Default", "string default value");
+ RNA_def_property_ui_text(prop, "Default", "String default value");
prop = RNA_def_property(srna, "length_max", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index da03921bca6..7cae88d292b 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -1812,7 +1812,9 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr)
static char *rna_SceneRenderView_path(PointerRNA *ptr)
{
SceneRenderView *srv = (SceneRenderView *)ptr->data;
- return BLI_sprintfN("render.views[\"%s\"]", srv->name);
+ char srv_name_esc[sizeof(srv->name) * 2];
+ BLI_str_escape(srv_name_esc, srv->name, sizeof(srv_name_esc));
+ return BLI_sprintfN("render.views[\"%s\"]", srv_name_esc);
}
static void rna_Scene_use_nodes_update(bContext *C, PointerRNA *ptr)
@@ -2198,6 +2200,11 @@ static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.curve_paint_settings");
}
+static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.sequencer_tool_settings");
+}
+
/* generic function to recalc geometry */
static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr))
{
@@ -3582,6 +3589,38 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "custom_bevel_profile_preset");
RNA_def_property_struct_type(prop, "CurveProfile");
RNA_def_property_ui_text(prop, "Curve Profile Widget", "Used for defining a profile's path");
+
+ /* Sequencer tool settings */
+ prop = RNA_def_property(srna, "sequencer_tool_settings", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "SequencerToolSettings");
+ RNA_def_property_ui_text(prop, "Sequencer Tool Settings", NULL);
+}
+
+static void rna_def_sequencer_tool_settings(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
+ {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"},
+ {SEQ_USE_ORIGINAL_SIZE,
+ "ORIGINAL",
+ 0,
+ "Use Original Size",
+ "Keep image at its original size"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "SequencerToolSettings", NULL);
+ RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path");
+ RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", "");
+
+ prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, scale_fit_methods);
+ RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method");
}
static void rna_def_unified_paint_settings(BlenderRNA *brna)
@@ -3995,15 +4034,9 @@ static void rna_def_view_layer_eevee(BlenderRNA *brna)
srna = RNA_def_struct(brna, "ViewLayerEEVEE", NULL);
RNA_def_struct_ui_text(srna, "Eevee Settings", "View layer settings for Eevee");
- prop = RNA_def_property(srna, "use_pass_volume_scatter", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_SCATTER);
- RNA_def_property_ui_text(prop, "Volume Scatter", "Deliver volume scattering pass");
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
-
- prop = RNA_def_property(srna, "use_pass_volume_transmittance", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(
- prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE);
- RNA_def_property_ui_text(prop, "Volume Transmittance", "Deliver volume transmittance pass");
+ prop = RNA_def_property(srna, "use_pass_volume_direct", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_LIGHT);
+ RNA_def_property_ui_text(prop, "Volume Light", "Deliver volume direct light pass");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
prop = RNA_def_property(srna, "use_pass_bloom", PROP_BOOLEAN, PROP_NONE);
@@ -4319,7 +4352,7 @@ void rna_def_view_layer_common(StructRNA *srna, const bool scene)
prop = RNA_def_property(srna, "use_pass_mist", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_MIST);
- RNA_def_property_ui_text(prop, "Mist", "Deliver mist factor pass (0.0-1.0)");
+ RNA_def_property_ui_text(prop, "Mist", "Deliver mist factor pass (0.0 to 1.0)");
if (scene) {
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
}
@@ -6366,7 +6399,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0, 1000.0);
RNA_def_property_ui_text(prop,
"Scale",
- "Instead of automatically normalizing to 0..1, "
+ "Instead of automatically normalizing to the range 0 to 1, "
"apply a user scale to the derivative map");
/* stamp */
@@ -7972,6 +8005,7 @@ void RNA_def_scene(BlenderRNA *brna)
rna_def_gpencil_interpolate(brna);
rna_def_unified_paint_settings(brna);
rna_def_curve_paint_settings(brna);
+ rna_def_sequencer_tool_settings(brna);
rna_def_statvis(brna);
rna_def_unit_settings(brna);
rna_def_scene_image_format_data(brna);
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index ab84dcb0aba..784172b3ac9 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -270,6 +270,8 @@ static void rna_Area_ui_type_update(bContext *C, PointerRNA *ptr)
st->space_subtype_set(area, area->butspacetype_subtype);
}
area->butspacetype_subtype = 0;
+
+ ED_area_tag_refresh(area);
}
static void rna_View2D_region_to_view(struct View2D *v2d, float x, float y, float result[2])
@@ -586,7 +588,7 @@ static void rna_def_screen(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_statusbar", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SCREEN_COLLAPSE_STATUSBAR);
- RNA_def_property_ui_text(prop, "Show Status Bar", "Show Status Bar");
+ RNA_def_property_ui_text(prop, "Show Status Bar", "Show status bar");
RNA_def_property_update(prop, 0, "rna_Screen_bar_update");
func = RNA_def_function(srna, "statusbar_info", "rna_Screen_statusbar_info_get");
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 9e7fbf2f9a9..eea6fd88ec7 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1201,8 +1201,11 @@ static void rna_SequenceModifier_name_set(PointerRNA *ptr, const char *value)
if (adt) {
char path[1024];
+ char seq_name_esc[(sizeof(seq->name) - 2) * 2];
+ BLI_str_escape(seq_name_esc, seq->name + 2, sizeof(seq_name_esc));
+
BLI_snprintf(
- path, sizeof(path), "sequence_editor.sequences_all[\"%s\"].modifiers", seq->name + 2);
+ path, sizeof(path), "sequence_editor.sequences_all[\"%s\"].modifiers", seq_name_esc);
BKE_animdata_fix_paths_rename(&scene->id, adt, NULL, path, oldname, smd->name, 0, 0, 1);
}
}
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 65ea155ffba..2f821dad811 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -170,6 +170,12 @@ const EnumPropertyItem rna_enum_space_sequencer_view_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_space_file_browse_mode_items[] = {
+ {FILE_BROWSE_MODE_FILES, "FILES", ICON_FILEBROWSER, "File Browser", ""},
+ {FILE_BROWSE_MODE_ASSETS, "ASSETS", ICON_ASSET_MANAGER, "Asset Browser", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#define SACT_ITEM_DOPESHEET \
{ \
SACTCONT_DOPESHEET, "DOPESHEET", ICON_ACTION, "Dope Sheet", "Edit all keyframes in scene" \
@@ -412,12 +418,7 @@ static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] =
{EEVEE_RENDER_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""},
{EEVEE_RENDER_PASS_SPECULAR_LIGHT, "SPECULAR_LIGHT", 0, "Specular Light", ""},
{EEVEE_RENDER_PASS_SPECULAR_COLOR, "SPECULAR_COLOR", 0, "Specular Color", ""},
- {EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE,
- "VOLUME_TRANSMITTANCE",
- 0,
- "Volume Transmittance",
- ""},
- {EEVEE_RENDER_PASS_VOLUME_SCATTER, "VOLUME_SCATTER", 0, "Volume Scattering", ""},
+ {EEVEE_RENDER_PASS_VOLUME_LIGHT, "VOLUME_LIGHT", 0, "Volume Light", ""},
{0, "", ICON_NONE, "Effects", ""},
{EEVEE_RENDER_PASS_BLOOM, "BLOOM", 0, "Bloom", ""},
@@ -509,6 +510,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = {
# include "BKE_layer.h"
# include "BKE_nla.h"
# include "BKE_paint.h"
+# include "BKE_preferences.h"
# include "BKE_scene.h"
# include "BKE_screen.h"
# include "BKE_workspace.h"
@@ -2467,13 +2469,172 @@ static PointerRNA rna_FileSelectParams_filter_id_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_FileSelectIDFilter, ptr->data);
}
+static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
+{
+ FileAssetSelectParams *params = ptr->data;
+ /* 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;
+}
+
+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;
+}
+
+static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
+{
+ FileSelectParams *params = ptr->data;
+ params->filter_id = value;
+}
+
+static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr)
+{
+ FileSelectParams *params = ptr->data;
+ return params->filter_id;
+}
+
+static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value)
+{
+ const FileDirEntry *entry = ptr->data;
+ strcpy(value, entry->name);
+}
+
+static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr)
+{
+ const FileDirEntry *entry = ptr->data;
+ return (int)strlen(entry->name);
+}
+
+static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr)
+{
+ const FileDirEntry *entry = ptr->data;
+ return ED_file_icon(entry);
+}
+
+static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr)
+{
+ const FileDirEntry *entry = ptr->data;
+ return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data);
+}
+
+static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr)
+{
+ SpaceFile *sfile = ptr->data;
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ if (params == ED_fileselect_get_file_params(sfile)) {
+ return &RNA_FileSelectParams;
+ }
+ if (params == (void *)ED_fileselect_get_asset_params(sfile)) {
+ return &RNA_FileAssetSelectParams;
+ }
+
+ BLI_assert(!"Could not identify file select parameters");
+ return NULL;
+}
+
static PointerRNA rna_FileBrowser_params_get(PointerRNA *ptr)
{
SpaceFile *sfile = ptr->data;
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ StructRNA *params_struct = rna_FileBrowser_params_typef(ptr);
- if (params) {
- return rna_pointer_inherit_refine(ptr, &RNA_FileSelectParams, params);
+ if (params && params_struct) {
+ return rna_pointer_inherit_refine(ptr, params_struct, params);
}
return rna_pointer_inherit_refine(ptr, NULL, NULL);
@@ -2789,6 +2950,14 @@ static void rna_FileBrowser_FSMenuRecent_active_range(
rna_FileBrowser_FSMenu_active_range(ptr, min, max, softmin, softmax, FS_CATEGORY_RECENT);
}
+static void rna_SpaceFileBrowser_browse_mode_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ ScrArea *area = rna_area_from_space(ptr);
+ ED_area_tag_refresh(area);
+}
+
#else
static const EnumPropertyItem dt_uv_items[] = {
@@ -4281,7 +4450,8 @@ static void rna_def_space_view3d(BlenderRNA *brna)
/* Camera Object Data. */
prop = RNA_def_property(srna, "show_gizmo_camera_lens", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_camera", V3D_GIZMO_SHOW_CAMERA_LENS);
- RNA_def_property_ui_text(prop, "Show Camera Lens", "Gizmo to adjust camera lens & ortho size");
+ RNA_def_property_ui_text(
+ prop, "Show Camera Lens", "Gizmo to adjust camera focal length or orthographic scale");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
prop = RNA_def_property(srna, "show_gizmo_camera_dof_distance", PROP_BOOLEAN, PROP_NONE);
@@ -5006,7 +5176,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, waveform_type_display_items);
- RNA_def_property_ui_text(prop, "Waveform Displaying", "How Waveforms are drawn");
+ RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are drawn");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE);
@@ -5059,6 +5229,27 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_FCURVES);
RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_OVERLAY);
+ RNA_def_property_ui_text(prop, "Show Overlay", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_NAME);
+ RNA_def_property_ui_text(prop, "Show Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_SOURCE);
+ RNA_def_property_ui_text(
+ prop, "Show Source", "Display path to source file, or name of source datablock");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_DURATION);
+ RNA_def_property_ui_text(prop, "Show Duration", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
}
static void rna_def_space_text(BlenderRNA *brna)
@@ -5491,7 +5682,7 @@ static void rna_def_space_graph(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_NORMALIZE);
RNA_def_property_ui_text(prop,
"Use Normalization",
- "Display curves in normalized to -1..1 range, "
+ "Display curves in normalized range from -1 to 1, "
"for easier editing of multiple curves with different ranges");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
@@ -5798,6 +5989,44 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
}
}
+static void rna_def_fileselect_entry(BlenderRNA *brna)
+{
+ PropertyRNA *prop;
+ StructRNA *srna = RNA_def_struct(brna, "FileSelectEntry", NULL);
+ RNA_def_struct_sdna(srna, "FileDirEntry");
+ RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop,
+ "rna_FileBrowser_FileSelectEntry_name_get",
+ "rna_FileBrowser_FileSelectEntry_name_length",
+ NULL);
+ RNA_def_property_ui_text(prop, "Name", "");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_int(
+ srna,
+ "preview_icon_id",
+ 0,
+ INT_MIN,
+ INT_MAX,
+ "Icon ID",
+ "Unique integer identifying the preview of this file as an icon (zero means invalid)",
+ INT_MIN,
+ INT_MAX);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(
+ prop, "rna_FileBrowser_FileSelectEntry_preview_icon_id_get", NULL, NULL);
+
+ prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "AssetMetaData");
+ RNA_def_property_pointer_funcs(
+ prop, "rna_FileBrowser_FileSelectEntry_asset_data_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "Asset Data", "Asset data, valid if the file represents an asset");
+}
+
static void rna_def_fileselect_params(BlenderRNA *brna)
{
StructRNA *srna;
@@ -5970,6 +6199,12 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_BLENDER, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ prop = RNA_def_property(srna, "use_filter_asset_only", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_ASSETS_ONLY);
+ RNA_def_property_ui_text(
+ prop, "Only Assets", "Hide .blend files items that are not data-blocks with asset metadata");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
prop = RNA_def_property(srna, "filter_id", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "FileSelectIDFilter");
@@ -6001,6 +6236,75 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
+static void rna_def_fileselect_asset_params(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* XXX copied from rna_def_fileselect_idfilter. */
+ 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"},
+ {FILTER_ID_OB | FILTER_ID_GR,
+ "OBJECTS_AND_COLLECTIONS",
+ ICON_GROUP,
+ "Objects & Collections",
+ "Show objects and collections"},
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME
+ /* XXX avoid warning */
+ // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO
+ ,
+ "GEOMETRY",
+ ICON_MESH_DATA,
+ "Geometry",
+ "Show meshes, curves, lattice, armatures and metaballs data"},
+ {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
+ "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,
+ "IMAGES_AND_SOUNDS",
+ 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 | FILTER_ID_WS,
+ "ENVIRONMENTS",
+ 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,
+ "MISC",
+ ICON_GREASEPENCIL,
+ "Miscellaneous",
+ "Show other data types"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "FileAssetSelectParams", "FileSelectParams");
+ 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");
+ RNA_def_property_ui_text(prop, "Asset Library", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
+ prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, asset_category_items);
+ RNA_def_property_enum_funcs(prop,
+ "rna_FileAssetSelectParams_asset_category_get",
+ "rna_FileAssetSelectParams_asset_category_set",
+ NULL);
+ RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+}
+
static void rna_def_filemenu_entry(BlenderRNA *brna)
{
StructRNA *srna;
@@ -6054,9 +6358,18 @@ static void rna_def_space_filebrowser(BlenderRNA *brna)
rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI));
+ prop = RNA_def_property(srna, "browse_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_space_file_browse_mode_items);
+ RNA_def_property_ui_text(
+ prop,
+ "Browsing Mode",
+ "Type of the File Editor view (regular file browsing or asset browsing)");
+ RNA_def_property_update(prop, 0, "rna_SpaceFileBrowser_browse_mode_update");
+
prop = RNA_def_property(srna, "params", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "FileSelectParams");
- RNA_def_property_pointer_funcs(prop, "rna_FileBrowser_params_get", NULL, NULL, NULL);
+ RNA_def_property_pointer_funcs(
+ prop, "rna_FileBrowser_params_get", NULL, "rna_FileBrowser_params_typef", NULL);
RNA_def_property_ui_text(
prop, "Filebrowser Parameter", "Parameters and Settings for the Filebrowser");
@@ -6804,7 +7117,9 @@ void RNA_def_space(BlenderRNA *brna)
rna_def_space_image(brna);
rna_def_space_sequencer(brna);
rna_def_space_text(brna);
+ rna_def_fileselect_entry(brna);
rna_def_fileselect_params(brna);
+ rna_def_fileselect_asset_params(brna);
rna_def_fileselect_idfilter(brna);
rna_def_filemenu_entry(brna);
rna_def_space_filebrowser(brna);
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index f1f7810bcf0..7a285df235a 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -184,6 +184,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "BKE_mesh_runtime.h"
# include "BKE_paint.h"
# include "BKE_pbvh.h"
+# include "BKE_preferences.h"
# include "BKE_screen.h"
# include "DEG_depsgraph.h"
@@ -335,6 +336,12 @@ static void rna_userdef_language_update(Main *UNUSED(bmain),
USERDEF_TAG_DIRTY;
}
+static void rna_userdef_asset_library_name_set(PointerRNA *ptr, const char *value)
+{
+ bUserAssetLibrary *library = (bUserAssetLibrary *)ptr->data;
+ BKE_preferences_asset_library_name_set(&U, library, value);
+}
+
static void rna_userdef_script_autoexec_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
@@ -4575,7 +4582,7 @@ static void rna_def_userdef_view(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Navigation Controls",
- "Show navigation controls in 2D & 3D views which do not have scroll bars");
+ "Show navigation controls in 2D and 3D views which do not have scroll bars");
RNA_def_property_update(prop, 0, "rna_userdef_gizmo_update");
/* menus */
@@ -5946,12 +5953,6 @@ static void rna_def_userdef_input(BlenderRNA *brna)
prop = RNA_def_property(srna, "invert_zoom_wheel", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_WHEELZOOMDIR);
RNA_def_property_ui_text(prop, "Wheel Invert Zoom", "Swap the Mouse Wheel zoom direction");
-
- prop = RNA_def_property(srna, "wheel_scroll_lines", PROP_INT, PROP_NONE);
- RNA_def_property_int_sdna(prop, NULL, "wheellinescroll");
- RNA_def_property_range(prop, 0, 32);
- RNA_def_property_ui_text(
- prop, "Wheel Scroll Lines", "Number of lines scrolled at a time with the mouse wheel");
}
static void rna_def_userdef_keymap(BlenderRNA *brna)
@@ -5974,6 +5975,30 @@ static void rna_def_userdef_keymap(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration");
}
+static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "UserAssetLibrary", NULL);
+ RNA_def_struct_sdna(srna, "bUserAssetLibrary");
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+ RNA_def_struct_ui_text(
+ srna, "Asset Library", "Settings to define a reusable library for Asset Browsers to use");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Name", "Identifier (not necessarily unique) for the asset library");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_userdef_asset_library_name_set");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "path", PROP_STRING, PROP_DIRPATH);
+ RNA_def_property_ui_text(
+ prop, "Path", "Path to a directory with .blend files to use as an asset library");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+}
+
static void rna_def_userdef_filepaths(BlenderRNA *brna)
{
PropertyRNA *prop;
@@ -5984,7 +6009,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
{2, "DJV", 0, "DJV", "Open source frame player: http://djv.sourceforge.net"},
{3, "FRAMECYCLER", 0, "FrameCycler", "Frame player from IRIDAS"},
{4, "RV", 0, "RV", "Frame player from Tweak Software"},
- {5, "MPLAYER", 0, "MPlayer", "Media player for video & png/jpeg/sgi image sequences"},
+ {5, "MPLAYER", 0, "MPlayer", "Media player for video and PNG/JPEG/SGI image sequences"},
{50, "CUSTOM", 0, "Custom", "Custom animation player executable path"},
{0, NULL, 0, NULL, NULL},
};
@@ -6067,10 +6092,11 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
prop = RNA_def_property(srna, "script_directory", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_sdna(prop, NULL, "pythondir");
- RNA_def_property_ui_text(prop,
- "Python Scripts Directory",
- "Alternate script path, matching the default layout with subdirs: "
- "startup, add-ons & modules (requires restart)");
+ RNA_def_property_ui_text(
+ prop,
+ "Python Scripts Directory",
+ "Alternate script path, matching the default layout with subdirectories: "
+ "startup, add-ons and modules (requires restart)");
/* TODO, editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
@@ -6125,7 +6151,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Auto Save Temporary Files",
"Automatic saving of temporary files in temp directory, "
- "uses process ID (sculpt & edit-mode data won't be saved!)");
+ "uses process ID (sculpt and edit mode data won't be saved)");
RNA_def_property_update(prop, 0, "rna_userdef_autosave_update");
prop = RNA_def_property(srna, "auto_save_time", PROP_INT, PROP_NONE);
@@ -6145,6 +6171,12 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Save Preview Images",
"Enables automatic saving of preview images in the .blend file");
+
+ rna_def_userdef_filepaths_asset_library(brna);
+
+ prop = RNA_def_property(srna, "asset_libraries", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "UserAssetLibrary");
+ RNA_def_property_ui_text(prop, "Asset Libraries", "");
}
static void rna_def_userdef_experimental(BlenderRNA *brna)
@@ -6165,6 +6197,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Undo Legacy",
"Use legacy undo (slower than the new default one, but may be more stable in some cases)");
+ prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1);
+ RNA_def_property_ui_text(
+ prop, "New Point Cloud Type", "Enable the new point cloud type in the ui");
+
prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1);
RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui");
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 26f31412724..3a7addcba94 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -78,6 +78,7 @@ set(SRC
intern/MOD_meshsequencecache.c
intern/MOD_mirror.c
intern/MOD_multires.c
+ intern/MOD_nodes.cc
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
@@ -88,7 +89,6 @@ set(SRC
intern/MOD_shapekey.c
intern/MOD_shrinkwrap.c
intern/MOD_simpledeform.c
- intern/MOD_nodes.cc
intern/MOD_skin.c
intern/MOD_smooth.c
intern/MOD_softbody.c
@@ -199,6 +199,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/modifiers/MOD_nodes.h b/source/blender/modifiers/MOD_nodes.h
index 9c75e7e3416..907dbe9c5f4 100644
--- a/source/blender/modifiers/MOD_nodes.h
+++ b/source/blender/modifiers/MOD_nodes.h
@@ -17,8 +17,8 @@
#pragma once
struct Main;
-struct Object;
struct NodesModifierData;
+struct Object;
#ifdef __cplusplus
extern "C" {
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 38fb19e3233..6769f14f88f 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -295,7 +295,7 @@ ModifierTypeInfo modifierType_Armature = {
/* deformMatricesEM */ deformMatricesEM,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index da1754b8ebd..412d6b87d82 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -1018,7 +1018,7 @@ ModifierTypeInfo modifierType_Array = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index 04ddac338e5..a94411d897e 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -447,7 +447,7 @@ ModifierTypeInfo modifierType_Bevel = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
index 0513d3af13a..e290fd9dab7 100644
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ b/source/blender/modifiers/intern/MOD_boolean.c
@@ -819,7 +819,7 @@ ModifierTypeInfo modifierType_Boolean = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 96ed0a5d069..0b1c661baed 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -346,7 +346,7 @@ ModifierTypeInfo modifierType_Build = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index 06bd9ada0fb..f905a38ae12 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -590,7 +590,7 @@ ModifierTypeInfo modifierType_Cast = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 8f876213cd6..a25d65347c5 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -33,6 +33,7 @@
#include "DNA_defaults.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -116,7 +117,7 @@ static void deformVerts(ModifierData *md,
mesh_src = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE);
}
- /* TODO(sergey): For now it actually duplicates logic from DerivedMesh.c
+ /* TODO(sergey): For now it actually duplicates logic from DerivedMesh.cc
* and needs some more generic solution. But starting experimenting with
* this so close to the release is not that nice..
*
@@ -309,7 +310,7 @@ ModifierTypeInfo modifierType_Cloth = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index faba08f9613..a80bac7c6de 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -23,6 +23,7 @@
#include "BLI_utildefines.h"
+#include "BLI_kdopbvh.h"
#include "BLI_math.h"
#include "BLT_translation.h"
@@ -30,6 +31,7 @@
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
@@ -309,7 +311,7 @@ ModifierTypeInfo modifierType_Collision = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 5884ec0aa17..15c4e8af6ce 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -851,7 +851,7 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index bed53cb0f51..d5d53edfd54 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -235,7 +235,7 @@ ModifierTypeInfo modifierType_Curve = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index d4c941d144d..8b299a82f95 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -494,7 +494,7 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 10ed4f8d80b..b28b8fd39fc 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -299,7 +299,7 @@ ModifierTypeInfo modifierType_Decimate = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index d432559fcfa..abe78943508 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -506,7 +506,7 @@ ModifierTypeInfo modifierType_Displace = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index b69179f464d..7f57183ff13 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -220,7 +220,7 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index 30c38623f68..e02befd7efa 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -186,7 +186,7 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index d5e065ad321..c12019a325e 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -1254,7 +1254,7 @@ ModifierTypeInfo modifierType_Explode = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 6dc4fe79f88..8a8d6f2305f 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -238,7 +238,7 @@ ModifierTypeInfo modifierType_Fluid = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index e0c362171f2..2fa05a319d5 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -573,7 +573,7 @@ ModifierTypeInfo modifierType_Hook = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index a484b4d8147..bda0f9ba5a4 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -888,7 +888,7 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index d51f95bd18d..fc527304e76 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -634,7 +634,7 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ init_data,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index 5aadc171394..e3c42e39dda 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -192,7 +192,7 @@ ModifierTypeInfo modifierType_Lattice = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 92ee5a84df9..191d39d9fce 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -462,7 +462,7 @@ ModifierTypeInfo modifierType_Mask = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
- /* modifyPointCloud */ nullptr,
+ /* modifyGeometrySet */ nullptr,
/* modifyVolume */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index e137a59e10f..cc007651733 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -295,7 +295,7 @@ ModifierTypeInfo modifierType_MeshToVolume = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyPointCloud */ nullptr,
+ /* modifyGeometrySet */ nullptr,
/* modifyVolume */ modifyVolume,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index b808d738fe8..6d2e0f242d7 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -388,7 +388,7 @@ ModifierTypeInfo modifierType_MeshCache = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index 0e530312238..4bd306e7679 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -646,7 +646,7 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 73106b2e816..2c01857adb1 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -270,7 +270,7 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index 9346f601981..7f34c6581ad 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -234,7 +234,7 @@ ModifierTypeInfo modifierType_Mirror = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 9f99e036601..1182c8db093 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -519,7 +519,7 @@ ModifierTypeInfo modifierType_Multires = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 80c69398d15..6bb747fa715 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -33,6 +33,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "DNA_collection_types.h"
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -104,6 +105,12 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
ids.add(&object->id);
}
}
+ else if (socket->type == SOCK_COLLECTION) {
+ Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value;
+ if (collection != nullptr) {
+ ids.add(&collection->id);
+ }
+ }
}
}
@@ -399,6 +406,11 @@ class GeometryNodesEvaluator {
blender::bke::PersistentObjectHandle object_handle = handle_map_.lookup(object);
new (buffer) blender::bke::PersistentObjectHandle(object_handle);
}
+ else if (bsocket->type == SOCK_COLLECTION) {
+ Collection *collection = ((bNodeSocketValueCollection *)bsocket->default_value)->value;
+ blender::bke::PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
+ new (buffer) blender::bke::PersistentCollectionHandle(collection_handle);
+ }
else {
blender::nodes::socket_cpp_value_get(*bsocket, buffer);
}
@@ -439,6 +451,8 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDProperty *prop = property_type.create_prop(socket, new_prop_name);
IDP_AddToGroup(settings_prop_group, prop);
+ prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
+
/* Make the group in the ui container group to hold the property's UI settings. */
IDProperty *prop_ui_group;
{
@@ -848,6 +862,9 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
else if (socket->type == SOCK_GEOMETRY) {
BKE_modifier_set_error(ob, md, "Node group can only have one geometry input");
}
+ else if (socket->type == SOCK_COLLECTION) {
+ BKE_modifier_set_error(ob, md, "Collection socket can not be exposed in the modifier");
+ }
else {
BKE_modifier_set_error(ob, md, "Missing property for input socket \"%s\"", socket->name);
}
@@ -934,9 +951,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return new_mesh;
}
-static void modifyPointCloud(ModifierData *md,
- const ModifierEvalContext *ctx,
- GeometrySet *geometry_set)
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
{
modifyGeometry(md, ctx, *geometry_set);
}
@@ -960,8 +977,12 @@ static void draw_property_for_socket(uiLayout *layout,
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
if (property != nullptr && property_type->is_correct_type(*property)) {
- char rna_path[128];
- BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket.identifier);
+
+ char socket_id_esc[sizeof(socket.identifier) * 2];
+ BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+
+ char rna_path[sizeof(socket_id_esc) + 4];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
uiItemR(layout, settings_ptr, rna_path, 0, socket.name, ICON_NONE);
}
}
@@ -1043,6 +1064,15 @@ static void freeData(ModifierData *md)
}
}
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *UNUSED(md),
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ /* We don't know what the node tree will need. If there are vertex groups, it is likely that the
+ * node tree wants to access them. */
+ r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+}
+
ModifierTypeInfo modifierType_Nodes = {
/* name */ "GeometryNodes",
/* structName */ "NodesModifierData",
@@ -1050,9 +1080,9 @@ ModifierTypeInfo modifierType_Nodes = {
/* srna */ &RNA_NodesModifier,
/* type */ eModifierTypeType_Constructive,
/* flags */
- static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh |
- eModifierTypeFlag_SupportsEditmode |
- eModifierTypeFlag_EnableInEditmode),
+ static_cast<ModifierTypeFlag>(
+ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode |
+ eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping),
/* icon */ ICON_NODETREE,
/* copyData */ copyData,
@@ -1063,11 +1093,11 @@ ModifierTypeInfo modifierType_Nodes = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
- /* modifyPointCloud */ modifyPointCloud,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* modifyVolume */ nullptr,
/* initData */ initData,
- /* requiredDataMask */ nullptr,
+ /* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index 02307622d06..4daa527577b 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -59,7 +59,7 @@ ModifierTypeInfo modifierType_None = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index 0ec564d2e2d..ec10b18665e 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -804,7 +804,7 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 5aef497c0c4..9c940745920 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -736,7 +736,7 @@ ModifierTypeInfo modifierType_Ocean = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index f660874a5ea..e7f1fa9943e 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -678,7 +678,7 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 86480a17083..e49f16b994c 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -331,7 +331,7 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index f8fc4ad658e..175435fcd44 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -300,7 +300,7 @@ ModifierTypeInfo modifierType_Remesh = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 6521a0859e5..ba370b401f3 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -701,8 +701,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/*printf("flip direction %i\n", ed_loop_flip);*/
- /* switch the flip option if set
- * note: flip is now done at face level so copying vgroup slizes is easier */
+ /* Switch the flip option if set
+ * NOTE: flip is now done at face level so copying group slices is easier. */
#if 0
if (do_flip) {
ed_loop_flip = !ed_loop_flip;
@@ -1259,7 +1259,7 @@ ModifierTypeInfo modifierType_Screw = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index bb267386dfb..81a0ee72496 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -27,6 +27,7 @@
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
#include "BKE_key.h"
#include "BKE_particle.h"
@@ -139,7 +140,7 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* deformMatricesEM */ deformMatricesEM,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index dddabb12f61..93626309727 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -292,7 +292,7 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index ec89176f97e..e41e70864dc 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -552,7 +552,7 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 6936f5a53f8..5e412185cea 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -2043,7 +2043,7 @@ ModifierTypeInfo modifierType_Skin = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index e73a59f1ae8..618ccc78279 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -32,6 +32,7 @@
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "BKE_context.h"
@@ -286,7 +287,7 @@ ModifierTypeInfo modifierType_Smooth = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 4bdb3ba60b1..9a657c44fca 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -119,7 +119,7 @@ ModifierTypeInfo modifierType_Softbody = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 8886d3718c9..8e519a72df1 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -29,6 +29,7 @@
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "BKE_context.h"
@@ -274,7 +275,7 @@ ModifierTypeInfo modifierType_Solidify = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index c8b357b34c8..99069919120 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -26,6 +26,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
index 8acf07f9181..f62980ec4fd 100644
--- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
+++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
@@ -24,6 +24,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 1aa015682dd..d089894a6e9 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -507,7 +507,7 @@ ModifierTypeInfo modifierType_Subsurf = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 72f19efe3a4..7416a4baf38 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -240,7 +240,7 @@ ModifierTypeInfo modifierType_Surface = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 5407397e3bf..e00c5ba7f04 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1546,7 +1546,7 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 1930a38b825..04d24ac0883 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -177,7 +177,7 @@ ModifierTypeInfo modifierType_Triangulate = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index fa5243c548f..55dbfdf478f 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -307,10 +307,16 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
/* Modifier Icon. */
sub = uiLayoutRow(layout, true);
+ uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
if (mti->isDisabled && mti->isDisabled(scene, md, 0)) {
uiLayoutSetRedAlert(sub, true);
}
- uiItemL(sub, "", RNA_struct_ui_icon(ptr->type));
+ uiItemStringO(sub,
+ "",
+ RNA_struct_ui_icon(ptr->type),
+ "OBJECT_OT_modifier_set_active",
+ "modifier",
+ md->name);
row = uiLayoutRow(layout, true);
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index bfbc27abb88..8122f4617c1 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -370,7 +370,7 @@ ModifierTypeInfo modifierType_UVProject = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index b326494815e..77b79167c2f 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -341,7 +341,7 @@ ModifierTypeInfo modifierType_UVWarp = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index 90e570f55fb..745e089b8ff 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -328,7 +328,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyPointCloud */ nullptr,
+ /* modifyGeometrySet */ nullptr,
/* modifyVolume */ modifyVolume,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index ec53914f115..941bc8409f7 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -335,7 +335,7 @@ ModifierTypeInfo modifierType_VolumeToMesh = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
- /* modifyPointCloud */ nullptr,
+ /* modifyGeometrySet */ nullptr,
/* modifyVolume */ nullptr,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 53e41484606..9d3d5b0658c 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -538,7 +538,7 @@ ModifierTypeInfo modifierType_Warp = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 45f06a7778c..863656b85a5 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -491,7 +491,7 @@ ModifierTypeInfo modifierType_Wave = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index bd15d909834..40265e37db9 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -763,7 +763,7 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index df554f6bc4e..915adccc745 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -427,7 +427,7 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 9be36fe6846..52cee7ce7e5 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -513,7 +513,7 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 7232ffd3d9d..aac29cabf0f 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -767,7 +767,7 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index e34dcf48c19..fd1254fc948 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -2052,7 +2052,7 @@ ModifierTypeInfo modifierType_Weld = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 206c514826c..3d8e74d2cf5 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -195,7 +195,7 @@ ModifierTypeInfo modifierType_Wireframe = {
/* deformMatricesEM */ NULL,
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
- /* modifyPointCloud */ NULL,
+ /* modifyGeometrySet */ NULL,
/* modifyVolume */ NULL,
/* initData */ initData,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index a367f40dca7..bc0e7972bcb 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -22,9 +22,9 @@ set(INC
.
composite
function
+ geometry
intern
shader
- geometry
texture
../blenkernel
../blenlib
@@ -138,16 +138,20 @@ set(SRC
function/nodes/node_fn_switch.cc
function/node_function_util.cc
+ geometry/nodes/node_geo_attribute_color_ramp.cc
+ geometry/nodes/node_geo_attribute_fill.cc
geometry/nodes/node_geo_attribute_math.cc
- geometry/nodes/node_geo_common.cc
+ geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_boolean.cc
+ geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_object_info.cc
- geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_point_distribute.cc
+ geometry/nodes/node_geo_point_distribute_poisson_disk.cc
geometry/nodes/node_geo_point_instance.cc
- geometry/nodes/node_geo_random_attribute.cc
+ geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/node_geometry_exec.cc
@@ -295,12 +299,12 @@ set(SRC
NOD_composite.h
NOD_derived_node_tree.hh
NOD_function.h
+ NOD_geometry.h
+ NOD_math_functions.hh
NOD_node_tree_dependencies.hh
NOD_node_tree_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
- NOD_geometry.h
- NOD_math_functions.hh
NOD_socket.h
NOD_static_types.h
NOD_texture.h
@@ -311,9 +315,9 @@ set(SRC
)
set(LIB
+ bf_bmesh
bf_functions
bf_intern_sky
- bf_bmesh
)
if(WITH_PYTHON)
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 0532547375f..f1cd55ce048 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -26,6 +26,7 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
+void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);
@@ -34,9 +35,11 @@ void register_node_type_geo_triangulate(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
void register_node_type_geo_object_info(void);
-void register_node_type_geo_random_attribute(void);
+void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_attribute_mix(void);
+void register_node_type_geo_attribute_color_ramp(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index a7df4bc3e1b..cac04e18fc7 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -26,12 +26,16 @@
namespace blender::nodes {
+using bke::BooleanReadAttribute;
+using bke::BooleanWriteAttribute;
using bke::Color4fReadAttribute;
using bke::Color4fWriteAttribute;
using bke::Float3ReadAttribute;
using bke::Float3WriteAttribute;
using bke::FloatReadAttribute;
using bke::FloatWriteAttribute;
+using bke::Int32ReadAttribute;
+using bke::Int32WriteAttribute;
using bke::PersistentDataHandleMap;
using bke::PersistentObjectHandle;
using bke::ReadAttribute;
@@ -168,10 +172,21 @@ class GeoNodeExecParams {
return this->get_input_attribute(name, component, domain, type, &default_value);
}
+ /**
+ * Get the type of an input property or the associated constant socket types with the
+ * same names. Fall back to the default value if no attribute exists with the name.
+ */
+ CustomDataType get_input_attribute_data_type(const StringRef name,
+ const GeometryComponent &component,
+ const CustomDataType default_type) const;
+
private:
/* Utilities for detecting common errors at when using this class. */
void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
void check_set_output(StringRef identifier, const CPPType &value_type) const;
+
+ /* Find the active socket socket with the input name (not the identifier). */
+ const bNodeSocket *find_available_socket(const StringRef name) const;
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 09e0908140c..662952abb59 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -271,12 +271,15 @@ DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Spl
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
-DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
-DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c
index c8d2d993e75..f5308fe2671 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c
@@ -27,172 +27,36 @@
#include "BLI_utildefines.h"
#include "node_composite_util.h"
-/* this is taken from the cryptomatte specification 1.0 */
-
-BLI_INLINE float hash_to_float(uint32_t hash)
+static CryptomatteEntry *cryptomatte_find(NodeCryptomatte *n, float encoded_hash)
{
- uint32_t mantissa = hash & ((1 << 23) - 1);
- uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
- exponent = MAX2(exponent, (uint32_t)1);
- exponent = MIN2(exponent, (uint32_t)254);
- exponent = exponent << 23;
- uint32_t sign = (hash >> 31);
- sign = sign << 31;
- uint32_t float_bits = sign | exponent | mantissa;
- float f;
- /* Bit casting relies on equal size for both types. */
- BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size")
- memcpy(&f, &float_bits, sizeof(float));
- return f;
+ LISTBASE_FOREACH (CryptomatteEntry *, entry, &n->entries) {
+ if (entry->encoded_hash == encoded_hash) {
+ return entry;
+ }
+ }
+ return NULL;
}
static void cryptomatte_add(NodeCryptomatte *n, float f)
{
- /* Turn the number into a string. */
- char number[32];
- BLI_snprintf(number, sizeof(number), "<%.9g>", f);
-
- /* Search if we already have the number. */
- if (n->matte_id && strlen(n->matte_id) != 0) {
- size_t start = 0;
- const size_t end = strlen(n->matte_id);
- size_t token_len = 0;
- while (start < end) {
- /* Ignore leading whitespace. */
- while (start < end && n->matte_id[start] == ' ') {
- start++;
- }
-
- /* Find the next separator. */
- char *token_end = strchr(n->matte_id + start, ',');
- if (ELEM(token_end, NULL, n->matte_id + start)) {
- token_end = n->matte_id + end;
- }
- /* Be aware that token_len still contains any trailing white space. */
- token_len = token_end - (n->matte_id + start);
-
- /* If this has a leading bracket,
- * assume a raw floating point number and look for the closing bracket. */
- if (n->matte_id[start] == '<') {
- if (strncmp(n->matte_id + start, number, strlen(number)) == 0) {
- /* This number is already there, so continue. */
- return;
- }
- }
- else {
- /* Remove trailing white space */
- size_t name_len = token_len;
- while (n->matte_id[start + name_len] == ' ' && name_len > 0) {
- name_len--;
- }
- /* Calculate the hash of the token and compare. */
- uint32_t hash = BLI_hash_mm3((const unsigned char *)(n->matte_id + start), name_len, 0);
- if (f == hash_to_float(hash)) {
- return;
- }
- }
- start += token_len + 1;
- }
- }
-
- DynStr *new_matte = BLI_dynstr_new();
- if (!new_matte) {
+ /* Check if entry already exist. */
+ if (cryptomatte_find(n, f) != NULL) {
return;
}
-
- if (n->matte_id) {
- BLI_dynstr_append(new_matte, n->matte_id);
- MEM_freeN(n->matte_id);
- }
-
- if (BLI_dynstr_get_len(new_matte) > 0) {
- BLI_dynstr_append(new_matte, ",");
- }
- BLI_dynstr_append(new_matte, number);
- n->matte_id = BLI_dynstr_get_cstring(new_matte);
- BLI_dynstr_free(new_matte);
+ CryptomatteEntry *entry = MEM_callocN(sizeof(CryptomatteEntry), __func__);
+ entry->encoded_hash = f;
+ BLI_addtail(&n->entries, entry);
}
static void cryptomatte_remove(NodeCryptomatte *n, float f)
{
- if (n->matte_id == NULL || strlen(n->matte_id) == 0) {
- /* Empty string, nothing to remove. */
+ CryptomatteEntry *entry = cryptomatte_find(n, f);
+ if (entry == NULL) {
return;
}
- /* This will be the new string without the removed key. */
- DynStr *new_matte = BLI_dynstr_new();
- if (!new_matte) {
- return;
- }
-
- /* Turn the number into a string. */
- static char number[32];
- BLI_snprintf(number, sizeof(number), "<%.9g>", f);
-
- /* Search if we already have the number. */
- size_t start = 0;
- const size_t end = strlen(n->matte_id);
- size_t token_len = 0;
- bool is_first = true;
- while (start < end) {
- bool skip = false;
- /* Ignore leading whitespace or commas. */
- while (start < end && ((n->matte_id[start] == ' ') || (n->matte_id[start] == ','))) {
- start++;
- }
-
- /* Find the next separator. */
- char *token_end = strchr(n->matte_id + start + 1, ',');
- if (ELEM(token_end, NULL, n->matte_id + start)) {
- token_end = n->matte_id + end;
- }
- /* Be aware that token_len still contains any trailing white space. */
- token_len = token_end - (n->matte_id + start);
-
- if (token_len == 1) {
- skip = true;
- }
- /* If this has a leading bracket,
- * assume a raw floating point number and look for the closing bracket. */
- else if (n->matte_id[start] == '<') {
- if (strncmp(n->matte_id + start, number, strlen(number)) == 0) {
- /* This number is already there, so skip it. */
- skip = true;
- }
- }
- else {
- /* Remove trailing white space */
- size_t name_len = token_len;
- while (n->matte_id[start + name_len] == ' ' && name_len > 0) {
- name_len--;
- }
- /* Calculate the hash of the token and compare. */
- uint32_t hash = BLI_hash_mm3((const unsigned char *)(n->matte_id + start), name_len, 0);
- if (f == hash_to_float(hash)) {
- skip = true;
- }
- }
- if (!skip) {
- if (is_first) {
- is_first = false;
- }
- else {
- BLI_dynstr_append(new_matte, ", ");
- }
- BLI_dynstr_nappend(new_matte, n->matte_id + start, token_len);
- }
- start += token_len + 1;
- }
-
- if (n->matte_id) {
- MEM_freeN(n->matte_id);
- n->matte_id = NULL;
- }
- if (BLI_dynstr_get_len(new_matte) > 0) {
- n->matte_id = BLI_dynstr_get_cstring(new_matte);
- }
- BLI_dynstr_free(new_matte);
+ BLI_remlink(&n->entries, entry);
+ MEM_freeN(entry);
}
static bNodeSocketTemplate outputs[] = {
@@ -265,10 +129,7 @@ static void node_free_cryptomatte(bNode *node)
NodeCryptomatte *nc = node->storage;
if (nc) {
- if (nc->matte_id) {
- MEM_freeN(nc->matte_id);
- }
-
+ BLI_freelistN(&nc->entries);
MEM_freeN(nc);
}
}
@@ -280,10 +141,7 @@ static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree),
NodeCryptomatte *src_nc = src_node->storage;
NodeCryptomatte *dest_nc = MEM_dupallocN(src_nc);
- if (src_nc->matte_id) {
- dest_nc->matte_id = MEM_dupallocN(src_nc->matte_id);
- }
-
+ BLI_duplicatelist(&dest_nc->entries, &src_nc->entries);
dest_node->storage = dest_nc;
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index ec389961615..c97463cdc22 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -42,4 +42,10 @@ namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
const StringRef name,
const GeometryNodeAttributeInputMode mode);
-}
+
+void poisson_disk_point_elimination(Vector<float3> const *input_points,
+ Vector<float3> *output_points,
+ float maximum_distance,
+ float3 boundbox);
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
new file mode 100644
index 00000000000..3f7023ba88f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -0,0 +1,107 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+#include "BKE_colorband.h"
+
+static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_color_ramp_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage;
+
+ const std::string result_name = params.get_input<std::string>("Result");
+ /* Once we support more domains at the user level, we have to decide how the result domain is
+ * choosen. */
+ const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+ const CustomDataType result_type = CD_PROP_COLOR;
+
+ WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ Color4fWriteAttribute attribute_out = std::move(attribute_result);
+
+ const std::string input_name = params.get_input<std::string>("Attribute");
+ FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ input_name, result_domain, 0.0f);
+
+ Span<float> data_in = attribute_in.get_span();
+ MutableSpan<Color4f> data_out = attribute_out.get_span();
+
+ ColorBand *color_ramp = &node_storage->color_ramp;
+ for (const int i : data_in.index_range()) {
+ BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
+ }
+
+ attribute_out.apply_span();
+}
+
+static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
+ sizeof(NodeAttributeColorRamp), __func__);
+ BKE_colorband_init(&node_storage->color_ramp, true);
+ node->storage = node_storage;
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_color_ramp()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_color_ramp_in, geo_node_attribute_color_ramp_out);
+ node_type_storage(
+ &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
new file mode 100644
index 00000000000..5cdbd18ecc8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BLI_rand.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_fill_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_fill_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = CD_PROP_FLOAT;
+}
+
+static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
+ bNodeSocket *socket_value_float = socket_value_vector->next;
+ bNodeSocket *socket_value_color4f = socket_value_float->next;
+ bNodeSocket *socket_value_boolean = socket_value_color4f->next;
+
+ const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
+
+ nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
+}
+
+namespace blender::nodes {
+
+static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ if (attribute_name.empty()) {
+ return;
+ }
+
+ WriteAttributePtr attribute = component.attribute_try_ensure_for_write(
+ attribute_name, domain, data_type);
+ if (!attribute) {
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ FloatWriteAttribute float_attribute = std::move(attribute);
+ const float value = params.get_input<float>("Value_001");
+ MutableSpan<float> attribute_span = float_attribute.get_span();
+ attribute_span.fill(value);
+ float_attribute.apply_span();
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ Float3WriteAttribute float3_attribute = std::move(attribute);
+ const float3 value = params.get_input<float3>("Value");
+ MutableSpan<float3> attribute_span = float3_attribute.get_span();
+ attribute_span.fill(value);
+ float3_attribute.apply_span();
+ break;
+ }
+ case CD_PROP_COLOR: {
+ Color4fWriteAttribute color4f_attribute = std::move(attribute);
+ const Color4f value = params.get_input<Color4f>("Value_002");
+ MutableSpan<Color4f> attribute_span = color4f_attribute.get_span();
+ attribute_span.fill(value);
+ color4f_attribute.apply_span();
+ break;
+ }
+ case CD_PROP_BOOL: {
+ BooleanWriteAttribute boolean_attribute = std::move(attribute);
+ const bool value = params.get_input<bool>("Value_003");
+ MutableSpan<bool> attribute_span = boolean_attribute.get_span();
+ attribute_span.fill(value);
+ boolean_attribute.apply_span();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_fill()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out);
+ node_type_init(&ntype, geo_node_attribute_fill_init);
+ node_type_update(&ntype, geo_node_attribute_fill_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
new file mode 100644
index 00000000000..2f2558a2d53
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -0,0 +1,221 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_material.h"
+
+#include "DNA_material_types.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_mix_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Factor")},
+ {SOCK_FLOAT, N_("Factor"), 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR},
+ {SOCK_STRING, N_("A")},
+ {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0},
+ {SOCK_STRING, N_("B")},
+ {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("B"), 0.5, 0.5, 0.5, 1.0},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_mix_attribute_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void do_mix_operation_float(const int blend_mode,
+ const FloatReadAttribute &factors,
+ const FloatReadAttribute &inputs_a,
+ const FloatReadAttribute &inputs_b,
+ FloatWriteAttribute &results)
+{
+ const int size = results.size();
+ for (const int i : IndexRange(size)) {
+ const float factor = factors[i];
+ float3 a{inputs_a[i]};
+ const float3 b{inputs_b[i]};
+ ramp_blend(blend_mode, a, factor, b);
+ const float result = a.length();
+ results.set(i, result);
+ }
+}
+
+static void do_mix_operation_float3(const int blend_mode,
+ const FloatReadAttribute &factors,
+ const Float3ReadAttribute &inputs_a,
+ const Float3ReadAttribute &inputs_b,
+ Float3WriteAttribute &results)
+{
+ const int size = results.size();
+ for (const int i : IndexRange(size)) {
+ const float factor = factors[i];
+ float3 a = inputs_a[i];
+ const float3 b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+}
+
+static void do_mix_operation_color4f(const int blend_mode,
+ const FloatReadAttribute &factors,
+ const Color4fReadAttribute &inputs_a,
+ const Color4fReadAttribute &inputs_b,
+ Color4fWriteAttribute &results)
+{
+ const int size = results.size();
+ for (const int i : IndexRange(size)) {
+ const float factor = factors[i];
+ Color4f a = inputs_a[i];
+ const Color4f b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+}
+
+static void do_mix_operation(const CustomDataType result_type,
+ int blend_mode,
+ const FloatReadAttribute &attribute_factor,
+ ReadAttributePtr attribute_a,
+ ReadAttributePtr attribute_b,
+ WriteAttributePtr attribute_result)
+{
+ if (result_type == CD_PROP_FLOAT) {
+ FloatReadAttribute attribute_a_float = std::move(attribute_a);
+ FloatReadAttribute attribute_b_float = std::move(attribute_b);
+ FloatWriteAttribute attribute_result_float = std::move(attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a_float,
+ attribute_b_float,
+ attribute_result_float);
+ }
+ else if (result_type == CD_PROP_FLOAT3) {
+ Float3ReadAttribute attribute_a_float3 = std::move(attribute_a);
+ Float3ReadAttribute attribute_b_float3 = std::move(attribute_b);
+ Float3WriteAttribute attribute_result_float3 = std::move(attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a_float3,
+ attribute_b_float3,
+ attribute_result_float3);
+ }
+ else if (result_type == CD_PROP_COLOR) {
+ Color4fReadAttribute attribute_a_color4f = std::move(attribute_a);
+ Color4fReadAttribute attribute_b_color4f = std::move(attribute_b);
+ Color4fWriteAttribute attribute_result_color4f = std::move(attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a_color4f,
+ attribute_b_color4f,
+ attribute_result_color4f);
+ }
+}
+
+static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ const NodeAttributeMix *node_storage = (const NodeAttributeMix *)node.storage;
+
+ CustomDataType result_type = CD_PROP_COLOR;
+ AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+
+ /* Use type and domain from the result attribute, if it exists already. */
+ const std::string result_name = params.get_input<std::string>("Result");
+ const ReadAttributePtr result_attribute_read = component.attribute_try_get_for_read(result_name);
+ if (result_attribute_read) {
+ result_type = result_attribute_read->custom_data_type();
+ result_domain = result_attribute_read->domain();
+ }
+
+ WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ "Factor", component, result_domain, 0.5f);
+ ReadAttributePtr attribute_a = params.get_input_attribute(
+ "A", component, result_domain, result_type, nullptr);
+ ReadAttributePtr attribute_b = params.get_input_attribute(
+ "B", component, result_domain, result_type, nullptr);
+
+ do_mix_operation(result_type,
+ node_storage->blend_type,
+ attribute_factor,
+ std::move(attribute_a),
+ std::move(attribute_b),
+ std::move(attribute_result));
+}
+
+static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
+ "attribute mix node");
+ data->blend_type = MA_RAMP_BLEND;
+ data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node->storage = data;
+}
+
+static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ update_attribute_input_socket_availabilities(
+ *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_mix()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_mix_in, geo_node_mix_attribute_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update);
+ node_type_storage(
+ &ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_mix_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 5cacb96412c..2c3acfc9735 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -16,12 +16,13 @@
#include "node_geometry_util.hh"
+#include "BLI_hash.h"
#include "BLI_rand.hh"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
-static bNodeSocketTemplate geo_node_random_attribute_in[] = {
+static bNodeSocketTemplate geo_node_attribute_randomize_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
{SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
@@ -32,17 +33,17 @@ static bNodeSocketTemplate geo_node_random_attribute_in[] = {
{-1, ""},
};
-static bNodeSocketTemplate geo_node_random_attribute_out[] = {
+static bNodeSocketTemplate geo_node_attribute_randomize_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
-static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node)
+static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
}
-static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -59,38 +60,85 @@ static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *no
namespace blender::nodes {
-static void randomize_attribute(FloatWriteAttribute &attribute,
- float min,
- float max,
- RandomNumberGenerator &rng)
+/** Rehash to combine the seed with the "id" hash and a mutator for each dimension. */
+static float noise_from_index_and_mutator(const int seed, const int hash, const int mutator)
+{
+ const int combined_hash = BLI_hash_int_3d(seed, hash, mutator);
+ return BLI_hash_int_01(combined_hash);
+}
+
+/** Rehash to combine the seed with the "id" hash. */
+static float noise_from_index(const int seed, const int hash)
+{
+ const int combined_hash = BLI_hash_int_2d(seed, hash);
+ return BLI_hash_int_01(combined_hash);
+}
+
+static void randomize_attribute(BooleanWriteAttribute &attribute, Span<int> hashes, const int seed)
+{
+ MutableSpan<bool> attribute_span = attribute.get_span();
+ for (const int i : IndexRange(attribute.size())) {
+ const bool value = noise_from_index(seed, hashes[i]) > 0.5f;
+ attribute_span[i] = value;
+ }
+ attribute.apply_span();
+}
+
+static void randomize_attribute(
+ FloatWriteAttribute &attribute, float min, float max, Span<int> hashes, const int seed)
{
MutableSpan<float> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
- const float value = rng.get_float() * (max - min) + min;
+ const float value = noise_from_index(seed, hashes[i]) * (max - min) + min;
attribute_span[i] = value;
}
attribute.apply_span();
}
-static void randomize_attribute(Float3WriteAttribute &attribute,
- float3 min,
- float3 max,
- RandomNumberGenerator &rng)
+static void randomize_attribute(
+ Float3WriteAttribute &attribute, float3 min, float3 max, Span<int> hashes, const int seed)
{
MutableSpan<float3> attribute_span = attribute.get_span();
for (const int i : IndexRange(attribute.size())) {
- const float x = rng.get_float();
- const float y = rng.get_float();
- const float z = rng.get_float();
+ const float x = noise_from_index_and_mutator(seed, hashes[i], 47);
+ const float y = noise_from_index_and_mutator(seed, hashes[i], 8);
+ const float z = noise_from_index_and_mutator(seed, hashes[i], 64);
const float3 value = float3(x, y, z) * (max - min) + min;
attribute_span[i] = value;
}
attribute.apply_span();
}
+static Array<int> get_element_hashes(GeometryComponent &component,
+ const AttributeDomain domain,
+ const int attribute_size)
+{
+ /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
+ ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
+ Array<int> hashes(attribute_size);
+ if (hash_attribute) {
+ BLI_assert(hashes.size() == hash_attribute->size());
+ const CPPType &cpp_type = hash_attribute->cpp_type();
+ fn::GSpan items = hash_attribute->get_span();
+ for (const int i : hashes.index_range()) {
+ hashes[i] = (int)cpp_type.hash(items[i]);
+ }
+ }
+ else {
+ /* If there is no "id" attribute for per-point variation, just create it here. */
+ RandomNumberGenerator rng;
+ rng.seed(0);
+ for (const int i : hashes.index_range()) {
+ hashes[i] = rng.get_int32();
+ }
+ }
+
+ return hashes;
+}
+
static void randomize_attribute(GeometryComponent &component,
const GeoNodeExecParams &params,
- RandomNumberGenerator &rng)
+ const int seed)
{
const bNode &node = params.node();
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
@@ -106,19 +154,26 @@ static void randomize_attribute(GeometryComponent &component,
return;
}
+ Array<int> hashes = get_element_hashes(component, domain, attribute->size());
+
switch (data_type) {
case CD_PROP_FLOAT: {
FloatWriteAttribute float_attribute = std::move(attribute);
const float min_value = params.get_input<float>("Min_001");
const float max_value = params.get_input<float>("Max_001");
- randomize_attribute(float_attribute, min_value, max_value, rng);
+ randomize_attribute(float_attribute, min_value, max_value, hashes, seed);
break;
}
case CD_PROP_FLOAT3: {
Float3WriteAttribute float3_attribute = std::move(attribute);
const float3 min_value = params.get_input<float3>("Min");
const float3 max_value = params.get_input<float3>("Max");
- randomize_attribute(float3_attribute, min_value, max_value, rng);
+ randomize_attribute(float3_attribute, min_value, max_value, hashes, seed);
+ break;
+ }
+ case CD_PROP_BOOL: {
+ BooleanWriteAttribute boolean_attribute = std::move(attribute);
+ randomize_attribute(boolean_attribute, hashes, seed);
break;
}
default:
@@ -132,14 +187,10 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
const int seed = params.get_input<int>("Seed");
if (geometry_set.has<MeshComponent>()) {
- RandomNumberGenerator rng;
- rng.seed_random(seed);
- randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng);
+ randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, seed);
}
if (geometry_set.has<PointCloudComponent>()) {
- RandomNumberGenerator rng;
- rng.seed_random(seed + 3245231);
- randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng);
+ randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, seed);
}
params.set_output("Geometry", geometry_set);
@@ -147,15 +198,16 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_random_attribute()
+void register_node_type_geo_attribute_randomize()
{
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", NODE_CLASS_ATTRIBUTE, 0);
- node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out);
- node_type_init(&ntype, geo_node_random_attribute_init);
- node_type_update(&ntype, geo_node_random_attribute_update);
+ &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_randomize_in, geo_node_attribute_randomize_out);
+ node_type_init(&ntype, geo_node_attribute_randomize_init);
+ node_type_update(&ntype, geo_node_attribute_randomize_update);
ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index dedc3213a1f..80ac45aed4e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -218,12 +218,12 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
for (const InstancesComponent *component : src_components) {
const int size = component->instances_amount();
- Span<const Object *> objects = component->objects();
+ Span<InstancedData> instanced_data = component->instanced_data();
Span<float3> positions = component->positions();
Span<float3> rotations = component->rotations();
Span<float3> scales = component->scales();
for (const int i : IndexRange(size)) {
- dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]);
+ dst_component.add_instance(instanced_data[i], positions[i], rotations[i], scales[i]);
}
}
}
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 2f5f7e264bc..1d3fbae5b2e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -24,6 +24,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
+#include "BKE_bvhutils.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -33,8 +34,10 @@
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Distance Min"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
+ {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
@@ -43,11 +46,20 @@ static bNodeSocketTemplate geo_node_point_distribute_out[] = {
{-1, ""},
};
+static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+
+ nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON));
+}
+
namespace blender::nodes {
-static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
- const float density,
- const FloatReadAttribute &density_factors)
+static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh,
+ const float density,
+ const FloatReadAttribute &density_factors,
+ Vector<int> &r_ids,
+ const int seed)
{
/* 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));
@@ -71,7 +83,7 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
3.0f;
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
- const int looptri_seed = BLI_hash_int(looptri_index);
+ const int looptri_seed = BLI_hash_int(looptri_index + seed);
RandomNumberGenerator looptri_rng(looptri_seed);
const float points_amount_fl = area * density * looptri_density_factor;
@@ -84,23 +96,178 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh,
float3 point_pos;
interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords);
points.append(point_pos);
+
+ /* Build a hash stable even when the mesh is deformed. */
+ r_ids.append(((int)(bary_coords.hash()) + looptri_index));
}
}
return points;
}
+struct RayCastAll_Data {
+ void *bvhdata;
+
+ BVHTree_RayCastCallback raycast_callback;
+
+ /** The original coordinate the result point was projected from. */
+ float2 raystart;
+
+ const Mesh *mesh;
+ float base_weight;
+ FloatReadAttribute *density_factors;
+ Vector<float3> *projected_points;
+ Vector<int> *stable_ids;
+ float cur_point_weight;
+};
+
+static void project_2d_bvh_callback(void *userdata,
+ int index,
+ const BVHTreeRay *ray,
+ BVHTreeRayHit *hit)
+{
+ struct RayCastAll_Data *data = (RayCastAll_Data *)userdata;
+ data->raycast_callback(data->bvhdata, index, ray, hit);
+ if (hit->index != -1) {
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(data->mesh));
+ const MVert *mvert = data->mesh->mvert;
+
+ const MLoopTri &looptri = looptris[index];
+ const FloatReadAttribute &density_factors = data->density_factors[0];
+
+ const int v0_index = data->mesh->mloop[looptri.tri[0]].v;
+ const int v1_index = data->mesh->mloop[looptri.tri[1]].v;
+ const int v2_index = data->mesh->mloop[looptri.tri[2]].v;
+
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_index]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_index]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_index]);
+
+ /* Calculate barycentric weights for hit point. */
+ float3 weights;
+ interp_weights_tri_v3(
+ weights, mvert[v0_index].co, mvert[v1_index].co, mvert[v2_index].co, hit->co);
+
+ float point_weight = weights[0] * v0_density_factor + weights[1] * v1_density_factor +
+ weights[2] * v2_density_factor;
+
+ point_weight *= data->base_weight;
+
+ if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) {
+ data->projected_points->append(hit->co);
+
+ /* Build a hash stable even when the mesh is deformed. */
+ data->stable_ids->append((int)data->raystart.hash());
+ }
+ }
+}
+
+static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh,
+ const float density,
+ const float minimum_distance,
+ const FloatReadAttribute &density_factors,
+ Vector<int> &r_ids,
+ const int seed)
+{
+ Vector<float3> points;
+
+ if (minimum_distance <= FLT_EPSILON || density <= FLT_EPSILON) {
+ return points;
+ }
+
+ /* Scatter points randomly on the mesh with higher density (5-7) times higher than desired for
+ * good quality possion disk distributions. */
+ int quality = 5;
+ const int output_points_target = 1000;
+ points.resize(output_points_target * quality);
+
+ const float required_area = output_points_target *
+ (2.0f * sqrtf(3.0f) * minimum_distance * minimum_distance);
+ const float point_scale_multiplier = sqrtf(required_area);
+
+ {
+ const int rnd_seed = BLI_hash_int(seed);
+ RandomNumberGenerator point_rng(rnd_seed);
+
+ for (int i = 0; i < points.size(); i++) {
+ points[i].x = point_rng.get_float() * point_scale_multiplier;
+ points[i].y = point_rng.get_float() * point_scale_multiplier;
+ points[i].z = 0.0f;
+ }
+ }
+
+ /* Eliminate the scattered points until we get a possion distribution. */
+ Vector<float3> output_points(output_points_target);
+
+ const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0);
+ poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max);
+ Vector<float3> final_points;
+ r_ids.reserve(output_points_target);
+ final_points.reserve(output_points_target);
+
+ /* Check if we have any points we should remove from the final possion distribition. */
+ BVHTreeFromMesh treedata;
+ BKE_bvhtree_from_mesh_get(&treedata, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 2);
+
+ float3 bb_min, bb_max;
+ BLI_bvhtree_get_bounding_box(treedata.tree, bb_min, bb_max);
+
+ struct RayCastAll_Data data;
+ data.bvhdata = &treedata;
+ data.raycast_callback = treedata.raycast_callback;
+ data.mesh = mesh;
+ data.projected_points = &final_points;
+ data.stable_ids = &r_ids;
+ data.density_factors = const_cast<FloatReadAttribute *>(&density_factors);
+ data.base_weight = std::min(
+ 1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier)));
+
+ const float max_dist = bb_max[2] - bb_min[2] + 2.0f;
+ const float3 dir = float3(0, 0, -1);
+ float3 raystart;
+ raystart.z = bb_max[2] + 1.0f;
+
+ float tile_start_x_coord = bb_min[0];
+ int tile_repeat_x = ceilf((bb_max[0] - bb_min[0]) / point_scale_multiplier);
+
+ float tile_start_y_coord = bb_min[1];
+ int tile_repeat_y = ceilf((bb_max[1] - bb_min[1]) / point_scale_multiplier);
+
+ for (int x = 0; x < tile_repeat_x; x++) {
+ float tile_curr_x_coord = x * point_scale_multiplier + tile_start_x_coord;
+ for (int y = 0; y < tile_repeat_y; y++) {
+ float tile_curr_y_coord = y * point_scale_multiplier + tile_start_y_coord;
+ for (int idx = 0; idx < output_points.size(); idx++) {
+ raystart.x = output_points[idx].x + tile_curr_x_coord;
+ raystart.y = output_points[idx].y + tile_curr_y_coord;
+
+ data.cur_point_weight = (float)idx / (float)output_points.size();
+ data.raystart = raystart;
+
+ BLI_bvhtree_ray_cast_all(
+ treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data);
+ }
+ }
+ }
+
+ return final_points;
+}
+
static void geo_node_point_distribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
+ GeometryNodePointDistributeMethod distribute_method =
+ static_cast<GeometryNodePointDistributeMethod>(params.node().custom1);
+
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set_out));
return;
}
- const float density = params.extract_input<float>("Density");
+ const float density = params.extract_input<float>("Density Max");
const std::string density_attribute = params.extract_input<std::string>("Density Attribute");
if (density <= 0.0f) {
@@ -113,8 +280,21 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>(
density_attribute, ATTR_DOMAIN_POINT, 1.0f);
+ const int seed = params.get_input<int>("Seed");
- Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors);
+ Vector<int> stable_ids;
+ Vector<float3> points;
+ switch (distribute_method) {
+ case GEO_NODE_POINT_DISTRIBUTE_RANDOM:
+ points = random_scatter_points_from_mesh(
+ mesh_in, density, density_factors, stable_ids, seed);
+ break;
+ case GEO_NODE_POINT_DISTRIBUTE_POISSON:
+ const float min_dist = params.extract_input<float>("Distance Min");
+ points = poisson_scatter_points_from_mesh(
+ mesh_in, density, min_dist, density_factors, stable_ids, seed);
+ break;
+ }
PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size());
memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size());
@@ -123,7 +303,16 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
pointcloud->radius[i] = 0.05f;
}
- geometry_set_out.replace_pointcloud(pointcloud);
+ PointCloudComponent &point_component =
+ geometry_set_out.get_component_for_write<PointCloudComponent>();
+ point_component.replace(pointcloud);
+
+ Int32WriteAttribute stable_id_attribute = point_component.attribute_try_ensure_for_write(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ MutableSpan<int> stable_ids_span = stable_id_attribute.get_span();
+ stable_ids_span.copy_from(stable_ids);
+ stable_id_attribute.apply_span();
+
params.set_output("Geometry", std::move(geometry_set_out));
}
} // namespace blender::nodes
@@ -135,6 +324,7 @@ void register_node_type_geo_point_distribute()
geo_node_type_base(
&ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out);
+ node_type_update(&ntype, node_point_distribute_update);
ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc
new file mode 100644
index 00000000000..47764efa15d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+/*
+ * Based on Cem Yuksel. 2015. Sample Elimination for Generating Poisson Disk Sample
+ * ! Sets. Computer Graphics Forum 34, 2 (May 2015), 25-32.
+ * ! http://www.cemyuksel.com/research/sampleelimination/
+ * Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com>
+ * All rights reserved.
+ */
+
+#include "BLI_inplace_priority_queue.hh"
+#include "BLI_kdtree.h"
+
+#include "node_geometry_util.hh"
+
+#include <iostream>
+#include <string.h>
+
+namespace blender::nodes {
+
+static void tile_point(Vector<float3> *tiled_points,
+ Vector<size_t> *indices,
+ const float maximum_distance,
+ const float3 boundbox,
+ float3 const &point,
+ size_t index,
+ int dimension = 0)
+{
+ for (int dimension_iter = dimension; dimension_iter < 3; dimension_iter++) {
+ if (boundbox[dimension_iter] - point[dimension_iter] < maximum_distance) {
+ float3 point_tiled = point;
+ point_tiled[dimension_iter] -= boundbox[dimension_iter];
+
+ tiled_points->append(point_tiled);
+ indices->append(index);
+
+ tile_point(tiled_points,
+ indices,
+ maximum_distance,
+ boundbox,
+ point_tiled,
+ index,
+ dimension_iter + 1);
+ }
+
+ if (point[dimension_iter] < maximum_distance) {
+ float3 point_tiled = point;
+ point_tiled[dimension_iter] += boundbox[dimension_iter];
+
+ tiled_points->append(point_tiled);
+ indices->append(index);
+
+ tile_point(tiled_points,
+ indices,
+ maximum_distance,
+ boundbox,
+ point_tiled,
+ index,
+ dimension_iter + 1);
+ }
+ }
+}
+
+/**
+ * Returns the weight the point gets based on the distance to another point.
+ */
+static float point_weight_influence_get(const float maximum_distance,
+ const float minimum_distance,
+ float distance)
+{
+ const float alpha = 8.0f;
+
+ if (distance < minimum_distance) {
+ distance = minimum_distance;
+ }
+
+ return std::pow(1.0f - distance / maximum_distance, alpha);
+}
+
+/**
+ * Weight each point based on their proximity to its neighbors
+ *
+ * For each index in the weight array add a weight based on the proximity the
+ * corresponding point has with its neighboors.
+ **/
+static void points_distance_weight_calculate(Vector<float> *weights,
+ const size_t point_id,
+ const float3 *input_points,
+ const void *kd_tree,
+ const float minimum_distance,
+ const float maximum_distance,
+ InplacePriorityQueue<float> *heap)
+{
+ KDTreeNearest_3d *nearest_point = nullptr;
+ int neighbors = BLI_kdtree_3d_range_search(
+ (KDTree_3d *)kd_tree, input_points[point_id], &nearest_point, maximum_distance);
+
+ for (int i = 0; i < neighbors; i++) {
+ size_t neighbor_point_id = nearest_point[i].index;
+
+ if (neighbor_point_id >= weights->size()) {
+ continue;
+ }
+
+ /* The point should not influence itself. */
+ if (neighbor_point_id == point_id) {
+ continue;
+ }
+
+ const float weight_influence = point_weight_influence_get(
+ maximum_distance, minimum_distance, nearest_point[i].dist);
+
+ /* In the first pass we just the weights. */
+ if (heap == nullptr) {
+ (*weights)[point_id] += weight_influence;
+ }
+ /* When we run again we need to update the weights and the heap. */
+ else {
+ (*weights)[neighbor_point_id] -= weight_influence;
+ heap->priority_decreased(neighbor_point_id);
+ }
+ }
+
+ if (nearest_point) {
+ MEM_freeN(nearest_point);
+ }
+}
+
+/**
+ * Returns the minimum radius fraction used by the default weight function.
+ */
+static float weight_limit_fraction_get(const size_t input_size, const size_t output_size)
+{
+ const float beta = 0.65f;
+ const float gamma = 1.5f;
+ float ratio = float(output_size) / float(input_size);
+ return (1.0f - std::pow(ratio, gamma)) * beta;
+}
+
+/**
+ * Tile the input points.
+ */
+static void points_tiling(const float3 *input_points,
+ const size_t input_size,
+ void **kd_tree,
+ const float maximum_distance,
+ const float3 boundbox)
+
+{
+ Vector<float3> tiled_points(input_points, input_points + input_size);
+ Vector<size_t> indices(input_size);
+
+ for (size_t i = 0; i < input_size; i++) {
+ indices[i] = i;
+ }
+
+ /* Tile the tree based on the boundbox. */
+ for (size_t i = 0; i < input_size; i++) {
+ tile_point(&tiled_points, &indices, maximum_distance, boundbox, input_points[i], i);
+ }
+
+ /* Build a new tree with the new indices and tiled points. */
+ *kd_tree = BLI_kdtree_3d_new(tiled_points.size());
+ for (size_t i = 0; i < tiled_points.size(); i++) {
+ BLI_kdtree_3d_insert(*(KDTree_3d **)kd_tree, indices[i], tiled_points[i]);
+ }
+ BLI_kdtree_3d_balance(*(KDTree_3d **)kd_tree);
+}
+
+static void weighted_sample_elimination(const float3 *input_points,
+ const size_t input_size,
+ float3 *output_points,
+ const size_t output_size,
+ const float maximum_distance,
+ const float3 boundbox,
+ const bool do_copy_eliminated)
+{
+ const float minimum_distance = maximum_distance *
+ weight_limit_fraction_get(input_size, output_size);
+
+ void *kd_tree = nullptr;
+ points_tiling(input_points, input_size, &kd_tree, maximum_distance, boundbox);
+
+ /* Assign weights to each sample. */
+ Vector<float> weights(input_size, 0.0f);
+ for (size_t point_id = 0; point_id < weights.size(); point_id++) {
+ points_distance_weight_calculate(
+ &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, nullptr);
+ }
+
+ /* Remove the points based on their weight. */
+ InplacePriorityQueue<float> heap(weights);
+
+ size_t sample_size = input_size;
+ while (sample_size > output_size) {
+ /* For each sample around it, remove its weight contribution and update the heap. */
+ size_t point_id = heap.pop_index();
+ points_distance_weight_calculate(
+ &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, &heap);
+ sample_size--;
+ }
+
+ /* Copy the samples to the output array. */
+ size_t target_size = do_copy_eliminated ? input_size : output_size;
+ for (size_t i = 0; i < target_size; i++) {
+ size_t index = heap.all_indices()[i];
+ output_points[i] = input_points[index];
+ }
+
+ /* Cleanup. */
+ BLI_kdtree_3d_free((KDTree_3d *)kd_tree);
+}
+
+static void progressive_sampling_reorder(Vector<float3> *output_points,
+ float maximum_density,
+ float3 boundbox)
+{
+ /* Re-order the points for progressive sampling. */
+ Vector<float3> temporary_points(output_points->size());
+ float3 *source_points = output_points->data();
+ float3 *dest_points = temporary_points.data();
+ size_t source_size = output_points->size();
+ size_t dest_size = 0;
+
+ while (source_size >= 3) {
+ dest_size = source_size * 0.5f;
+
+ /* Changes the weight function radius using half of the number of samples.
+ * It is used for progressive sampling. */
+ maximum_density *= std::sqrt(2.0f);
+ weighted_sample_elimination(
+ source_points, source_size, dest_points, dest_size, maximum_density, boundbox, true);
+
+ if (dest_points != output_points->data()) {
+ memcpy((*output_points)[dest_size],
+ dest_points[dest_size],
+ (source_size - dest_size) * sizeof(float3));
+ }
+
+ /* Swap the arrays around. */
+ float3 *points_iter = source_points;
+ source_points = dest_points;
+ dest_points = points_iter;
+ source_size = dest_size;
+ }
+ if (source_points != output_points->data()) {
+ memcpy(output_points->data(), source_points, dest_size * sizeof(float3));
+ }
+}
+
+void poisson_disk_point_elimination(Vector<float3> const *input_points,
+ Vector<float3> *output_points,
+ float maximum_density,
+ float3 boundbox)
+{
+ weighted_sample_elimination(input_points->data(),
+ input_points->size(),
+ output_points->data(),
+ output_points->size(),
+ maximum_density,
+ boundbox,
+ false);
+
+ progressive_sampling_reorder(output_points, maximum_density, boundbox);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index 6d979e3b7da..e030bc3eec6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -25,6 +25,7 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_OBJECT, N_("Object")},
+ {SOCK_COLLECTION, N_("Collection")},
{-1, ""},
};
@@ -35,9 +36,21 @@ static bNodeSocketTemplate geo_node_point_instance_out[] = {
namespace blender::nodes {
+static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
+{
+ bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *collection_socket = object_socket->next;
+
+ GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node->custom1;
+
+ nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
+ nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
+}
+
static void add_instances_from_geometry_component(InstancesComponent &instances,
const GeometryComponent &src_geometry,
- Object *object)
+ Object *object,
+ Collection *collection)
{
Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -47,30 +60,51 @@ static void add_instances_from_geometry_component(InstancesComponent &instances,
"scale", ATTR_DOMAIN_POINT, {1, 1, 1});
for (const int i : IndexRange(positions.size())) {
- instances.add_instance(object, positions[i], rotations[i], scales[i]);
+ if (object != nullptr) {
+ instances.add_instance(object, positions[i], rotations[i], scales[i]);
+ }
+ if (collection != nullptr) {
+ instances.add_instance(collection, positions[i], rotations[i], scales[i]);
+ }
}
}
static void geo_node_point_instance_exec(GeoNodeExecParams params)
{
+ GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)params.node().custom1;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
- bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = nullptr;
+ Collection *collection = nullptr;
- if (object != nullptr && object != params.self_object()) {
- InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- if (geometry_set.has<MeshComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), object);
- }
- if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object);
+ if (type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT) {
+ bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
+ "Object");
+ object = params.handle_map().lookup(object_handle);
+ /* Avoid accidental recursion of instances. */
+ if (object == params.self_object()) {
+ object = nullptr;
}
}
+ else if (type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) {
+ bke::PersistentCollectionHandle collection_handle =
+ params.extract_input<bke::PersistentCollectionHandle>("Collection");
+ collection = params.handle_map().lookup(collection_handle);
+ }
+
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ if (geometry_set.has<MeshComponent>()) {
+ add_instances_from_geometry_component(
+ instances, *geometry_set.get_component_for_read<MeshComponent>(), object, collection);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ add_instances_from_geometry_component(
+ instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ object,
+ collection);
+ }
params.set_output("Geometry", std::move(geometry_set_out));
}
@@ -82,6 +116,7 @@ void register_node_type_geo_point_instance()
geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
+ node_type_update(&ntype, blender::nodes::geo_node_point_instance_update);
ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
index cc5e9547a96..5a6faa809f2 100644
--- a/source/blender/nodes/intern/math_functions.cc
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -41,6 +41,8 @@ const FloatMathOperationInfo *get_float_math_operation_info(const int operation)
RETURN_OPERATION_INFO("Sine", "math_sine");
case NODE_MATH_COSINE:
RETURN_OPERATION_INFO("Cosine", "math_cosine");
+ case NODE_MATH_TANGENT:
+ RETURN_OPERATION_INFO("Tangent", "math_tangent");
case NODE_MATH_ARCSINE:
RETURN_OPERATION_INFO("Arc Sine", "math_arcsine");
case NODE_MATH_ARCCOSINE:
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a6d9115f01f..eef2c6c9125 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -19,23 +19,31 @@
namespace blender::nodes {
-ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const
+const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- const bNodeSocket *found_socket = nullptr;
LISTBASE_FOREACH (const bNodeSocket *, socket, &node_.inputs) {
if ((socket->flag & SOCK_UNAVAIL) != 0) {
continue;
}
if (name == socket->name) {
- found_socket = socket;
- break;
+ return socket;
}
}
- BLI_assert(found_socket != nullptr);
+
+ return nullptr;
+}
+
+ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const
+{
+ const bNodeSocket *found_socket = this->find_available_socket(name);
+ BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
+ if (found_socket == nullptr) {
+ return component.attribute_get_constant_for_read(domain, type, default_value);
+ }
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
@@ -60,6 +68,42 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
return component.attribute_get_constant_for_read(domain, type, default_value);
}
+CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
+ const StringRef name,
+ const GeometryComponent &component,
+ const CustomDataType default_type) const
+{
+ const bNodeSocket *found_socket = this->find_available_socket(name);
+ BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
+ if (found_socket == nullptr) {
+ return default_type;
+ }
+
+ if (found_socket->type == SOCK_STRING) {
+ const std::string name = this->get_input<std::string>(found_socket->identifier);
+ ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
+ if (!attribute) {
+ return default_type;
+ }
+ return attribute->custom_data_type();
+ }
+ if (found_socket->type == SOCK_FLOAT) {
+ return CD_PROP_FLOAT;
+ }
+ if (found_socket->type == SOCK_VECTOR) {
+ return CD_PROP_FLOAT3;
+ }
+ if (found_socket->type == SOCK_RGBA) {
+ return CD_PROP_COLOR;
+ }
+ if (found_socket->type == SOCK_BOOLEAN) {
+ return CD_PROP_BOOL;
+ }
+
+ BLI_assert(false);
+ return default_type;
+}
+
void GeoNodeExecParams::check_extract_input(StringRef identifier,
const CPPType *requested_type) const
{
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index ebc70956147..60c2d6c37e1 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -37,6 +37,8 @@
#include "BKE_node.h"
#include "BKE_persistent_data_handle.hh"
+#include "DNA_collection_types.h"
+
#include "RNA_access.h"
#include "RNA_types.h"
@@ -280,6 +282,15 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *dval = (bNodeSocketValueCollection *)MEM_callocN(
+ sizeof(bNodeSocketValueCollection), "node socket value object");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
+ break;
+ break;
+ }
}
}
@@ -352,6 +363,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to->default_value;
+ bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -648,6 +666,7 @@ class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
};
MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
+MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle);
static bNodeSocketType *make_socket_type_object()
{
@@ -673,6 +692,16 @@ static bNodeSocketType *make_socket_type_geometry()
return socktype;
}
+static bNodeSocketType *make_socket_type_collection()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
+ socktype->get_cpp_type = []() {
+ /* Objects are not passed along as raw pointers, but as handles. */
+ return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>();
+ };
+ return socktype;
+}
+
void register_standard_node_socket_types(void)
{
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
@@ -711,5 +740,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_geometry());
+ nodeRegisterSocketType(make_socket_type_collection());
+
nodeRegisterSocketType(make_socket_type_virtual());
}
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
index ec5527a2970..2e4196af156 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -208,6 +208,10 @@ static DataTypeConversions create_implicit_conversions()
conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
add_implicit_conversion<Color4f, float>(
conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
+ add_implicit_conversion<float3, bool>(
+ conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; });
+ add_implicit_conversion<bool, float3>(
+ conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
return conversions;
}
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 123347afc19..9669dc6496b 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -451,6 +451,14 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_COLLECTION: {
+ switch (from) {
+ case SOCK_COLLECTION:
+ return 1;
+ default:
+ return -1;
+ }
+ }
default:
return -1;
}
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 46b4dbc96d7..366d801a888 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -26,7 +26,6 @@ struct ID; /* DNA_ID.h */
struct ListBase; /* DNA_listBase.h */
struct Object; /* DNA_object_types.h */
struct PathResolvedRNA;
-struct ReportList;
struct Text; /* defined in DNA_text_types.h */
struct bConstraint; /* DNA_constraint_types.h */
struct bConstraintOb; /* DNA_constraint_types.h */
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 63fb685e66d..fd996c8a1a2 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -28,6 +28,8 @@
#include "BKE_idprop.h"
+#include "DNA_ID.h" /* ID property definitions. */
+
#define USE_STRING_COERCE
#ifdef USE_STRING_COERCE
diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c
index 81d1cb7055d..9fb7aea162a 100644
--- a/source/blender/python/gpu/gpu_py_batch.c
+++ b/source/blender/python/gpu/gpu_py_batch.c
@@ -48,7 +48,7 @@
/** \name Utility Functions
* \{ */
-static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self)
+static bool py_batch_is_program_or_error(BPyGPUBatch *self)
{
if (!self->batch->shader) {
PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it");
@@ -63,7 +63,7 @@ static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self)
/** \name GPUBatch Type
* \{ */
-static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
+static PyObject *py_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
@@ -121,7 +121,7 @@ static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, Py
return (PyObject *)ret;
}
-PyDoc_STRVAR(bpygpu_Batch_vertbuf_add_doc,
+PyDoc_STRVAR(py_Batch_vertbuf_add_doc,
".. method:: vertbuf_add(buf)\n"
"\n"
" Add another vertex buffer to the Batch.\n"
@@ -134,7 +134,7 @@ PyDoc_STRVAR(bpygpu_Batch_vertbuf_add_doc,
" :param buf: The vertex buffer that will be added to the batch.\n"
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
);
-static PyObject *bpygpu_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf)
+static PyObject *py_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf)
{
if (!BPyGPUVertBuf_Check(py_buf)) {
PyErr_Format(PyExc_TypeError, "Expected a GPUVertBuf, got %s", Py_TYPE(py_buf)->tp_name);
@@ -167,7 +167,7 @@ static PyObject *bpygpu_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_b
}
PyDoc_STRVAR(
- bpygpu_Batch_program_set_doc,
+ py_Batch_program_set_doc,
".. method:: program_set(program)\n"
"\n"
" Assign a shader to this batch that will be used for drawing when not overwritten later.\n"
@@ -177,7 +177,7 @@ PyDoc_STRVAR(
"\n"
" :param program: The program/shader the batch will use in future draw calls.\n"
" :type program: :class:`gpu.types.GPUShader`\n");
-static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
+static PyObject *py_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
{
if (!BPyGPUShader_Check(py_shader)) {
PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name);
@@ -208,7 +208,7 @@ static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_sh
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_Batch_draw_doc,
+PyDoc_STRVAR(py_Batch_draw_doc,
".. method:: draw(program=None)\n"
"\n"
" Run the drawing program with the parameters assigned to the batch.\n"
@@ -216,7 +216,7 @@ PyDoc_STRVAR(bpygpu_Batch_draw_doc,
" :param program: Program that performs the drawing operations.\n"
" If ``None`` is passed, the last program set to this batch will run.\n"
" :type program: :class:`gpu.types.GPUShader`\n");
-static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args)
+static PyObject *py_Batch_draw(BPyGPUBatch *self, PyObject *args)
{
BPyGPUShader *py_program = NULL;
@@ -224,7 +224,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args)
return NULL;
}
if (py_program == NULL) {
- if (!bpygpu_batch_is_program_or_error(self)) {
+ if (!py_batch_is_program_or_error(self)) {
return NULL;
}
}
@@ -236,42 +236,42 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args)
Py_RETURN_NONE;
}
-static PyObject *bpygpu_Batch_program_use_begin(BPyGPUBatch *self)
+static PyObject *py_Batch_program_use_begin(BPyGPUBatch *self)
{
- if (!bpygpu_batch_is_program_or_error(self)) {
+ if (!py_batch_is_program_or_error(self)) {
return NULL;
}
GPU_shader_bind(self->batch->shader);
Py_RETURN_NONE;
}
-static PyObject *bpygpu_Batch_program_use_end(BPyGPUBatch *self)
+static PyObject *py_Batch_program_use_end(BPyGPUBatch *self)
{
- if (!bpygpu_batch_is_program_or_error(self)) {
+ if (!py_batch_is_program_or_error(self)) {
return NULL;
}
GPU_shader_unbind();
Py_RETURN_NONE;
}
-static struct PyMethodDef bpygpu_Batch_methods[] = {
- {"vertbuf_add", (PyCFunction)bpygpu_Batch_vertbuf_add, METH_O, bpygpu_Batch_vertbuf_add_doc},
- {"program_set", (PyCFunction)bpygpu_Batch_program_set, METH_O, bpygpu_Batch_program_set_doc},
- {"draw", (PyCFunction)bpygpu_Batch_draw, METH_VARARGS, bpygpu_Batch_draw_doc},
- {"_program_use_begin", (PyCFunction)bpygpu_Batch_program_use_begin, METH_NOARGS, ""},
- {"_program_use_end", (PyCFunction)bpygpu_Batch_program_use_end, METH_NOARGS, ""},
+static struct PyMethodDef py_Batch_methods[] = {
+ {"vertbuf_add", (PyCFunction)py_Batch_vertbuf_add, METH_O, py_Batch_vertbuf_add_doc},
+ {"program_set", (PyCFunction)py_Batch_program_set, METH_O, py_Batch_program_set_doc},
+ {"draw", (PyCFunction)py_Batch_draw, METH_VARARGS, py_Batch_draw_doc},
+ {"_program_use_begin", (PyCFunction)py_Batch_program_use_begin, METH_NOARGS, ""},
+ {"_program_use_end", (PyCFunction)py_Batch_program_use_end, METH_NOARGS, ""},
{NULL, NULL, 0, NULL},
};
#ifdef USE_GPU_PY_REFERENCES
-static int bpygpu_Batch_traverse(BPyGPUBatch *self, visitproc visit, void *arg)
+static int py_Batch_traverse(BPyGPUBatch *self, visitproc visit, void *arg)
{
Py_VISIT(self->references);
return 0;
}
-static int bpygpu_Batch_clear(BPyGPUBatch *self)
+static int py_Batch_clear(BPyGPUBatch *self)
{
Py_CLEAR(self->references);
return 0;
@@ -279,14 +279,14 @@ static int bpygpu_Batch_clear(BPyGPUBatch *self)
#endif
-static void bpygpu_Batch_dealloc(BPyGPUBatch *self)
+static void py_Batch_dealloc(BPyGPUBatch *self)
{
GPU_batch_discard(self->batch);
#ifdef USE_GPU_PY_REFERENCES
if (self->references) {
PyObject_GC_UnTrack(self);
- bpygpu_Batch_clear(self);
+ py_Batch_clear(self);
Py_XDECREF(self->references);
}
#endif
@@ -319,17 +319,17 @@ PyDoc_STRVAR(
PyTypeObject BPyGPUBatch_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUBatch",
.tp_basicsize = sizeof(BPyGPUBatch),
- .tp_dealloc = (destructor)bpygpu_Batch_dealloc,
+ .tp_dealloc = (destructor)py_Batch_dealloc,
#ifdef USE_GPU_PY_REFERENCES
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_doc = py_gpu_batch_doc,
- .tp_traverse = (traverseproc)bpygpu_Batch_traverse,
- .tp_clear = (inquiry)bpygpu_Batch_clear,
+ .tp_traverse = (traverseproc)py_Batch_traverse,
+ .tp_clear = (inquiry)py_Batch_clear,
#else
.tp_flags = Py_TPFLAGS_DEFAULT,
#endif
- .tp_methods = bpygpu_Batch_methods,
- .tp_new = bpygpu_Batch_new,
+ .tp_methods = py_Batch_methods,
+ .tp_new = py_Batch_new,
};
/** \} */
diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c
index c863c9a586f..0a1aecde986 100644
--- a/source/blender/python/gpu/gpu_py_element.c
+++ b/source/blender/python/gpu/gpu_py_element.c
@@ -39,7 +39,7 @@
/** \name IndexBuf Type
* \{ */
-static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
+static PyObject *py_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
@@ -175,7 +175,7 @@ static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder));
}
-static void bpygpu_IndexBuf_dealloc(BPyGPUIndexBuf *self)
+static void py_IndexBuf_dealloc(BPyGPUIndexBuf *self)
{
GPU_indexbuf_discard(self->elem);
Py_TYPE(self)->tp_free(self);
@@ -199,10 +199,10 @@ PyDoc_STRVAR(py_gpu_element_doc,
PyTypeObject BPyGPUIndexBuf_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUIndexBuf",
.tp_basicsize = sizeof(BPyGPUIndexBuf),
- .tp_dealloc = (destructor)bpygpu_IndexBuf_dealloc,
+ .tp_dealloc = (destructor)py_IndexBuf_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = py_gpu_element_doc,
- .tp_new = bpygpu_IndexBuf_new,
+ .tp_new = py_IndexBuf_new,
};
/** \} */
diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c
index 93cca78bad9..a479f270770 100644
--- a/source/blender/python/gpu/gpu_py_matrix.c
+++ b/source/blender/python/gpu/gpu_py_matrix.c
@@ -44,7 +44,7 @@
/** \name Helper Functions
* \{ */
-static bool bpygpu_stack_is_push_model_view_ok_or_error(void)
+static bool py_stack_is_push_model_view_ok_or_error(void)
{
if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
PyErr_SetString(
@@ -55,7 +55,7 @@ static bool bpygpu_stack_is_push_model_view_ok_or_error(void)
return true;
}
-static bool bpygpu_stack_is_push_projection_ok_or_error(void)
+static bool py_stack_is_push_projection_ok_or_error(void)
{
if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
PyErr_SetString(
@@ -66,7 +66,7 @@ static bool bpygpu_stack_is_push_projection_ok_or_error(void)
return true;
}
-static bool bpygpu_stack_is_pop_model_view_ok_or_error(void)
+static bool py_stack_is_pop_model_view_ok_or_error(void)
{
if (GPU_matrix_stack_level_get_model_view() == 0) {
PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached");
@@ -75,7 +75,7 @@ static bool bpygpu_stack_is_pop_model_view_ok_or_error(void)
return true;
}
-static bool bpygpu_stack_is_pop_projection_ok_or_error(void)
+static bool py_stack_is_pop_projection_ok_or_error(void)
{
if (GPU_matrix_stack_level_get_projection() == 0) {
PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
@@ -90,52 +90,52 @@ static bool bpygpu_stack_is_pop_projection_ok_or_error(void)
/** \name Manage Stack
* \{ */
-PyDoc_STRVAR(bpygpu_matrix_push_doc,
+PyDoc_STRVAR(py_matrix_push_doc,
".. function:: push()\n"
"\n"
" Add to the model-view matrix stack.\n");
-static PyObject *bpygpu_matrix_push(PyObject *UNUSED(self))
+static PyObject *py_matrix_push(PyObject *UNUSED(self))
{
- if (!bpygpu_stack_is_push_model_view_ok_or_error()) {
+ if (!py_stack_is_push_model_view_ok_or_error()) {
return NULL;
}
GPU_matrix_push();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_pop_doc,
+PyDoc_STRVAR(py_matrix_pop_doc,
".. function:: pop()\n"
"\n"
" Remove the last model-view matrix from the stack.\n");
-static PyObject *bpygpu_matrix_pop(PyObject *UNUSED(self))
+static PyObject *py_matrix_pop(PyObject *UNUSED(self))
{
- if (!bpygpu_stack_is_pop_model_view_ok_or_error()) {
+ if (!py_stack_is_pop_model_view_ok_or_error()) {
return NULL;
}
GPU_matrix_pop();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_push_projection_doc,
+PyDoc_STRVAR(py_matrix_push_projection_doc,
".. function:: push_projection()\n"
"\n"
" Add to the projection matrix stack.\n");
-static PyObject *bpygpu_matrix_push_projection(PyObject *UNUSED(self))
+static PyObject *py_matrix_push_projection(PyObject *UNUSED(self))
{
- if (!bpygpu_stack_is_push_projection_ok_or_error()) {
+ if (!py_stack_is_push_projection_ok_or_error()) {
return NULL;
}
GPU_matrix_push_projection();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_pop_projection_doc,
+PyDoc_STRVAR(py_matrix_pop_projection_doc,
".. function:: pop_projection()\n"
"\n"
" Remove the last projection matrix from the stack.\n");
-static PyObject *bpygpu_matrix_pop_projection(PyObject *UNUSED(self))
+static PyObject *py_matrix_pop_projection(PyObject *UNUSED(self))
{
- if (!bpygpu_stack_is_pop_projection_ok_or_error()) {
+ if (!py_stack_is_pop_projection_ok_or_error()) {
return NULL;
}
GPU_matrix_pop_projection();
@@ -162,12 +162,12 @@ enum {
PYGPU_MATRIX_TYPE_PROJECTION = 2,
};
-static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self);
-static PyObject *bpygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
+static PyObject *py_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self);
+static PyObject *py_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
-static PyMethodDef bpygpu_matrix_stack_context_methods[] = {
- {"__enter__", (PyCFunction)bpygpu_matrix_stack_context_enter, METH_NOARGS},
- {"__exit__", (PyCFunction)bpygpu_matrix_stack_context_exit, METH_VARARGS},
+static PyMethodDef py_matrix_stack_context_methods[] = {
+ {"__enter__", (PyCFunction)py_matrix_stack_context_enter, METH_NOARGS},
+ {"__exit__", (PyCFunction)py_matrix_stack_context_exit, METH_VARARGS},
{NULL},
};
@@ -175,10 +175,10 @@ static PyTypeObject BPyGPU_matrix_stack_context_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUMatrixStackContext",
.tp_basicsize = sizeof(BPyGPU_MatrixStackContext),
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_methods = bpygpu_matrix_stack_context_methods,
+ .tp_methods = py_matrix_stack_context_methods,
};
-static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
+static PyObject *py_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
{
/* sanity - should never happen */
if (self->level != -1) {
@@ -187,14 +187,14 @@ static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *se
}
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
- if (!bpygpu_stack_is_push_model_view_ok_or_error()) {
+ if (!py_stack_is_push_model_view_ok_or_error()) {
return NULL;
}
GPU_matrix_push();
self->level = GPU_matrix_stack_level_get_model_view();
}
else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
- if (!bpygpu_stack_is_push_projection_ok_or_error()) {
+ if (!py_stack_is_push_projection_ok_or_error()) {
return NULL;
}
GPU_matrix_push_projection();
@@ -206,8 +206,8 @@ static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *se
Py_RETURN_NONE;
}
-static PyObject *bpygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self,
- PyObject *UNUSED(args))
+static PyObject *py_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self,
+ PyObject *UNUSED(args))
{
/* sanity - should never happen */
if (self->level == -1) {
@@ -240,7 +240,7 @@ finally:
Py_RETURN_NONE;
}
-static PyObject *bpygpu_matrix_push_pop_impl(int type)
+static PyObject *py_matrix_push_pop_impl(int type)
{
BPyGPU_MatrixStackContext *ret = PyObject_New(BPyGPU_MatrixStackContext,
&BPyGPU_matrix_stack_context_Type);
@@ -250,23 +250,23 @@ static PyObject *bpygpu_matrix_push_pop_impl(int type)
}
PyDoc_STRVAR(
- bpygpu_matrix_push_pop_doc,
+ py_matrix_push_pop_doc,
".. function:: push_pop()\n"
"\n"
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
-static PyObject *bpygpu_matrix_push_pop(PyObject *UNUSED(self))
+static PyObject *py_matrix_push_pop(PyObject *UNUSED(self))
{
- return bpygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
+ return py_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
}
PyDoc_STRVAR(
- bpygpu_matrix_push_pop_projection_doc,
+ py_matrix_push_pop_projection_doc,
".. function:: push_pop_projection()\n"
"\n"
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n");
-static PyObject *bpygpu_matrix_push_pop_projection(PyObject *UNUSED(self))
+static PyObject *py_matrix_push_pop_projection(PyObject *UNUSED(self))
{
- return bpygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
+ return py_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
}
/** \} */
@@ -275,14 +275,14 @@ static PyObject *bpygpu_matrix_push_pop_projection(PyObject *UNUSED(self))
/** \name Manipulate State
* \{ */
-PyDoc_STRVAR(bpygpu_matrix_multiply_matrix_doc,
+PyDoc_STRVAR(py_matrix_multiply_matrix_doc,
".. function:: multiply_matrix(matrix)\n"
"\n"
" Multiply the current stack matrix.\n"
"\n"
" :param matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value)
{
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
@@ -292,14 +292,14 @@ static PyObject *bpygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_scale_doc,
+PyDoc_STRVAR(py_matrix_scale_doc,
".. function:: scale(scale)\n"
"\n"
" Scale the current stack matrix.\n"
"\n"
" :param scale: Scale the current stack matrix.\n"
" :type scale: sequence of 2 or 3 floats\n");
-static PyObject *bpygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_scale(PyObject *UNUSED(self), PyObject *value)
{
float scale[3];
int len;
@@ -316,12 +316,12 @@ static PyObject *bpygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_scale_uniform_doc,
+PyDoc_STRVAR(py_matrix_scale_uniform_doc,
".. function:: scale_uniform(scale)\n"
"\n"
" :param scale: Scale the current stack matrix.\n"
" :type scale: float\n");
-static PyObject *bpygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value)
{
float scalar;
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
@@ -332,14 +332,14 @@ static PyObject *bpygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *v
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_translate_doc,
+PyDoc_STRVAR(py_matrix_translate_doc,
".. function:: translate(offset)\n"
"\n"
" Scale the current stack matrix.\n"
"\n"
" :param offset: Translate the current stack matrix.\n"
" :type offset: sequence of 2 or 3 floats\n");
-static PyObject *bpygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_translate(PyObject *UNUSED(self), PyObject *value)
{
float offset[3];
int len;
@@ -362,34 +362,34 @@ static PyObject *bpygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value
/** \name Write State
* \{ */
-PyDoc_STRVAR(bpygpu_matrix_reset_doc,
+PyDoc_STRVAR(py_matrix_reset_doc,
".. function:: reset()\n"
"\n"
" Empty stack and set to identity.\n");
-static PyObject *bpygpu_matrix_reset(PyObject *UNUSED(self))
+static PyObject *py_matrix_reset(PyObject *UNUSED(self))
{
GPU_matrix_reset();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_load_identity_doc,
+PyDoc_STRVAR(py_matrix_load_identity_doc,
".. function:: load_identity()\n"
"\n"
" Empty stack and set to identity.\n");
-static PyObject *bpygpu_matrix_load_identity(PyObject *UNUSED(self))
+static PyObject *py_matrix_load_identity(PyObject *UNUSED(self))
{
GPU_matrix_identity_set();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_load_matrix_doc,
+PyDoc_STRVAR(py_matrix_load_matrix_doc,
".. function:: load_matrix(matrix)\n"
"\n"
" Load a matrix into the stack.\n"
"\n"
" :param matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value)
{
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
@@ -399,14 +399,14 @@ static PyObject *bpygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *val
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_matrix_load_projection_matrix_doc,
+PyDoc_STRVAR(py_matrix_load_projection_matrix_doc,
".. function:: load_projection_matrix(matrix)\n"
"\n"
" Load a projection matrix into the stack.\n"
"\n"
" :param matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value)
{
MatrixObject *pymat;
if (!Matrix_Parse4x4(value, &pymat)) {
@@ -422,42 +422,42 @@ static PyObject *bpygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), Py
/** \name Read State
* \{ */
-PyDoc_STRVAR(bpygpu_matrix_get_projection_matrix_doc,
+PyDoc_STRVAR(py_matrix_get_projection_matrix_doc,
".. function:: get_projection_matrix()\n"
"\n"
" Return a copy of the projection matrix.\n"
"\n"
" :return: A 4x4 projection matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_get_projection_matrix(PyObject *UNUSED(self))
+static PyObject *py_matrix_get_projection_matrix(PyObject *UNUSED(self))
{
float matrix[4][4];
GPU_matrix_projection_get(matrix);
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
}
-PyDoc_STRVAR(bpygpu_matrix_get_model_view_matrix_doc,
+PyDoc_STRVAR(py_matrix_get_model_view_matrix_doc,
".. function:: get_model_view_matrix()\n"
"\n"
" Return a copy of the model-view matrix.\n"
"\n"
" :return: A 4x4 view matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_get_model_view_matrix(PyObject *UNUSED(self))
+static PyObject *py_matrix_get_model_view_matrix(PyObject *UNUSED(self))
{
float matrix[4][4];
GPU_matrix_model_view_get(matrix);
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL);
}
-PyDoc_STRVAR(bpygpu_matrix_get_normal_matrix_doc,
+PyDoc_STRVAR(py_matrix_get_normal_matrix_doc,
".. function:: get_normal_matrix()\n"
"\n"
" Return a copy of the normal matrix.\n"
"\n"
" :return: A 3x3 normal matrix.\n"
" :rtype: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
+static PyObject *py_matrix_get_normal_matrix(PyObject *UNUSED(self))
{
float matrix[3][3];
GPU_matrix_normal_get(matrix);
@@ -470,81 +470,78 @@ static PyObject *bpygpu_matrix_get_normal_matrix(PyObject *UNUSED(self))
/** \name Module
* \{ */
-static struct PyMethodDef bpygpu_matrix_methods[] = {
+static struct PyMethodDef py_matrix_methods[] = {
/* Manage Stack */
- {"push", (PyCFunction)bpygpu_matrix_push, METH_NOARGS, bpygpu_matrix_push_doc},
- {"pop", (PyCFunction)bpygpu_matrix_pop, METH_NOARGS, bpygpu_matrix_pop_doc},
+ {"push", (PyCFunction)py_matrix_push, METH_NOARGS, py_matrix_push_doc},
+ {"pop", (PyCFunction)py_matrix_pop, METH_NOARGS, py_matrix_pop_doc},
{"push_projection",
- (PyCFunction)bpygpu_matrix_push_projection,
+ (PyCFunction)py_matrix_push_projection,
METH_NOARGS,
- bpygpu_matrix_push_projection_doc},
+ py_matrix_push_projection_doc},
{"pop_projection",
- (PyCFunction)bpygpu_matrix_pop_projection,
+ (PyCFunction)py_matrix_pop_projection,
METH_NOARGS,
- bpygpu_matrix_pop_projection_doc},
+ py_matrix_pop_projection_doc},
/* Stack (Context Manager) */
- {"push_pop", (PyCFunction)bpygpu_matrix_push_pop, METH_NOARGS, bpygpu_matrix_push_pop_doc},
+ {"push_pop", (PyCFunction)py_matrix_push_pop, METH_NOARGS, py_matrix_push_pop_doc},
{"push_pop_projection",
- (PyCFunction)bpygpu_matrix_push_pop_projection,
+ (PyCFunction)py_matrix_push_pop_projection,
METH_NOARGS,
- bpygpu_matrix_push_pop_projection_doc},
+ py_matrix_push_pop_projection_doc},
/* Manipulate State */
{"multiply_matrix",
- (PyCFunction)bpygpu_matrix_multiply_matrix,
+ (PyCFunction)py_matrix_multiply_matrix,
METH_O,
- bpygpu_matrix_multiply_matrix_doc},
- {"scale", (PyCFunction)bpygpu_matrix_scale, METH_O, bpygpu_matrix_scale_doc},
- {"scale_uniform",
- (PyCFunction)bpygpu_matrix_scale_uniform,
- METH_O,
- bpygpu_matrix_scale_uniform_doc},
- {"translate", (PyCFunction)bpygpu_matrix_translate, METH_O, bpygpu_matrix_translate_doc},
+ py_matrix_multiply_matrix_doc},
+ {"scale", (PyCFunction)py_matrix_scale, METH_O, py_matrix_scale_doc},
+ {"scale_uniform", (PyCFunction)py_matrix_scale_uniform, METH_O, py_matrix_scale_uniform_doc},
+ {"translate", (PyCFunction)py_matrix_translate, METH_O, py_matrix_translate_doc},
/* TODO */
#if 0
- {"rotate", (PyCFunction)bpygpu_matrix_rotate, METH_O, bpygpu_matrix_rotate_doc},
- {"rotate_axis", (PyCFunction)bpygpu_matrix_rotate_axis, METH_O, bpygpu_matrix_rotate_axis_doc},
- {"look_at", (PyCFunction)bpygpu_matrix_look_at, METH_O, bpygpu_matrix_look_at_doc},
+ {"rotate", (PyCFunction)py_matrix_rotate, METH_O, py_matrix_rotate_doc},
+ {"rotate_axis", (PyCFunction)py_matrix_rotate_axis, METH_O, py_matrix_rotate_axis_doc},
+ {"look_at", (PyCFunction)py_matrix_look_at, METH_O, py_matrix_look_at_doc},
#endif
/* Write State */
- {"reset", (PyCFunction)bpygpu_matrix_reset, METH_NOARGS, bpygpu_matrix_reset_doc},
+ {"reset", (PyCFunction)py_matrix_reset, METH_NOARGS, py_matrix_reset_doc},
{"load_identity",
- (PyCFunction)bpygpu_matrix_load_identity,
+ (PyCFunction)py_matrix_load_identity,
METH_NOARGS,
- bpygpu_matrix_load_identity_doc},
- {"load_matrix", (PyCFunction)bpygpu_matrix_load_matrix, METH_O, bpygpu_matrix_load_matrix_doc},
+ py_matrix_load_identity_doc},
+ {"load_matrix", (PyCFunction)py_matrix_load_matrix, METH_O, py_matrix_load_matrix_doc},
{"load_projection_matrix",
- (PyCFunction)bpygpu_matrix_load_projection_matrix,
+ (PyCFunction)py_matrix_load_projection_matrix,
METH_O,
- bpygpu_matrix_load_projection_matrix_doc},
+ py_matrix_load_projection_matrix_doc},
/* Read State */
{"get_projection_matrix",
- (PyCFunction)bpygpu_matrix_get_projection_matrix,
+ (PyCFunction)py_matrix_get_projection_matrix,
METH_NOARGS,
- bpygpu_matrix_get_projection_matrix_doc},
+ py_matrix_get_projection_matrix_doc},
{"get_model_view_matrix",
- (PyCFunction)bpygpu_matrix_get_model_view_matrix,
+ (PyCFunction)py_matrix_get_model_view_matrix,
METH_NOARGS,
- bpygpu_matrix_get_model_view_matrix_doc},
+ py_matrix_get_model_view_matrix_doc},
{"get_normal_matrix",
- (PyCFunction)bpygpu_matrix_get_normal_matrix,
+ (PyCFunction)py_matrix_get_normal_matrix,
METH_NOARGS,
- bpygpu_matrix_get_normal_matrix_doc},
+ py_matrix_get_normal_matrix_doc},
{NULL, NULL, 0, NULL},
};
-PyDoc_STRVAR(bpygpu_matrix_doc, "This module provides access to the matrix stack.");
+PyDoc_STRVAR(py_matrix_doc, "This module provides access to the matrix stack.");
static PyModuleDef BPyGPU_matrix_module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "gpu.matrix",
- .m_doc = bpygpu_matrix_doc,
- .m_methods = bpygpu_matrix_methods,
+ .m_doc = py_matrix_doc,
+ .m_methods = py_matrix_methods,
};
PyObject *BPyInit_gpu_matrix(void)
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 6d14dd0de5f..00d367c603b 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -58,9 +58,9 @@
/** \name GPUOffScreen Common Utilities
* \{ */
-static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs)
+static int py_offscreen_valid_check(BPyGPUOffScreen *py_ofs)
{
- if (UNLIKELY(bpygpu_ofs->ofs == NULL)) {
+ if (UNLIKELY(py_ofs->ofs == NULL)) {
PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
return -1;
}
@@ -69,7 +69,7 @@ static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs)
#define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \
{ \
- if (UNLIKELY(bpygpu_offscreen_valid_check(bpygpu) == -1)) { \
+ if (UNLIKELY(py_offscreen_valid_check(bpygpu) == -1)) { \
return NULL; \
} \
} \
@@ -81,7 +81,7 @@ static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs)
/** \name GPUOffscreen Type
* \{ */
-static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds)
+static PyObject *py_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
@@ -112,23 +112,23 @@ static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args
return BPyGPUOffScreen_CreatePyObject(ofs);
}
-PyDoc_STRVAR(bpygpu_offscreen_width_doc, "Width of the texture.\n\n:type: `int`");
-static PyObject *bpygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
+PyDoc_STRVAR(py_offscreen_width_doc, "Width of the texture.\n\n:type: `int`");
+static PyObject *py_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
return PyLong_FromLong(GPU_offscreen_width(self->ofs));
}
-PyDoc_STRVAR(bpygpu_offscreen_height_doc, "Height of the texture.\n\n:type: `int`");
-static PyObject *bpygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
+PyDoc_STRVAR(py_offscreen_height_doc, "Height of the texture.\n\n:type: `int`");
+static PyObject *py_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
return PyLong_FromLong(GPU_offscreen_height(self->ofs));
}
-PyDoc_STRVAR(bpygpu_offscreen_color_texture_doc,
+PyDoc_STRVAR(py_offscreen_color_texture_doc,
"OpenGL bindcode for the color texture.\n\n:type: `int`");
-static PyObject *bpygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type))
+static PyObject *py_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type))
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
@@ -136,7 +136,7 @@ static PyObject *bpygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void
}
PyDoc_STRVAR(
- bpygpu_offscreen_bind_doc,
+ py_offscreen_bind_doc,
".. method:: bind(save=True)\n"
"\n"
" Bind the offscreen object.\n"
@@ -145,7 +145,7 @@ PyDoc_STRVAR(
"\n"
" :arg save: Save the current OpenGL state, so that it can be restored when unbinding.\n"
" :type save: `bool`\n");
-static PyObject *bpygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
+static PyObject *py_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
bool save = true;
@@ -165,7 +165,7 @@ static PyObject *bpygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, Py
return (PyObject *)self;
}
-PyDoc_STRVAR(bpygpu_offscreen_unbind_doc,
+PyDoc_STRVAR(py_offscreen_unbind_doc,
".. method:: unbind(restore=True)\n"
"\n"
" Unbind the offscreen object.\n"
@@ -173,7 +173,7 @@ PyDoc_STRVAR(bpygpu_offscreen_unbind_doc,
" :arg restore: Restore the OpenGL state, can only be used when the state has been "
"saved before.\n"
" :type restore: `bool`\n");
-static PyObject *bpygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
+static PyObject *py_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
{
bool restore = true;
@@ -191,7 +191,7 @@ static PyObject *bpygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args,
}
PyDoc_STRVAR(
- bpygpu_offscreen_draw_view3d_doc,
+ py_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
"\n"
" Draw the 3d viewport in the offscreen object.\n"
@@ -208,9 +208,7 @@ PyDoc_STRVAR(
" :type view_matrix: :class:`mathutils.Matrix`\n"
" :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
" :type projection_matrix: :class:`mathutils.Matrix`\n");
-static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self,
- PyObject *args,
- PyObject *kwds)
+static PyObject *py_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
{
MatrixObject *py_mat_view, *py_mat_projection;
PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
@@ -272,12 +270,12 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self,
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_offscreen_free_doc,
+PyDoc_STRVAR(py_offscreen_free_doc,
".. method:: free()\n"
"\n"
" Free the offscreen object.\n"
" The framebuffer, texture and render objects will no longer be accessible.\n");
-static PyObject *bpygpu_offscreen_free(BPyGPUOffScreen *self)
+static PyObject *py_offscreen_free(BPyGPUOffScreen *self)
{
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
@@ -286,12 +284,12 @@ static PyObject *bpygpu_offscreen_free(BPyGPUOffScreen *self)
Py_RETURN_NONE;
}
-static PyObject *bpygpu_offscreen_bind_context_enter(BPyGPUOffScreen *UNUSED(self))
+static PyObject *py_offscreen_bind_context_enter(BPyGPUOffScreen *UNUSED(self))
{
Py_RETURN_NONE;
}
-static PyObject *bpygpu_offscreen_bind_context_exit(BPyGPUOffScreen *self, PyObject *UNUSED(args))
+static PyObject *py_offscreen_bind_context_exit(BPyGPUOffScreen *self, PyObject *UNUSED(args))
{
GPU_offscreen_unbind(self->ofs, self->is_saved);
Py_RETURN_NONE;
@@ -305,41 +303,34 @@ static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
Py_TYPE(self)->tp_free((PyObject *)self);
}
-static PyGetSetDef bpygpu_offscreen_getseters[] = {
+static PyGetSetDef py_offscreen_getseters[] = {
{"color_texture",
- (getter)bpygpu_offscreen_color_texture_get,
+ (getter)py_offscreen_color_texture_get,
(setter)NULL,
- bpygpu_offscreen_color_texture_doc,
- NULL},
- {"width", (getter)bpygpu_offscreen_width_get, (setter)NULL, bpygpu_offscreen_width_doc, NULL},
- {"height",
- (getter)bpygpu_offscreen_height_get,
- (setter)NULL,
- bpygpu_offscreen_height_doc,
+ py_offscreen_color_texture_doc,
NULL},
+ {"width", (getter)py_offscreen_width_get, (setter)NULL, py_offscreen_width_doc, NULL},
+ {"height", (getter)py_offscreen_height_get, (setter)NULL, py_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
-static struct PyMethodDef bpygpu_offscreen_methods[] = {
- {"bind",
- (PyCFunction)bpygpu_offscreen_bind,
- METH_VARARGS | METH_KEYWORDS,
- bpygpu_offscreen_bind_doc},
+static struct PyMethodDef py_offscreen_methods[] = {
+ {"bind", (PyCFunction)py_offscreen_bind, METH_VARARGS | METH_KEYWORDS, py_offscreen_bind_doc},
{"unbind",
- (PyCFunction)bpygpu_offscreen_unbind,
+ (PyCFunction)py_offscreen_unbind,
METH_VARARGS | METH_KEYWORDS,
- bpygpu_offscreen_unbind_doc},
+ py_offscreen_unbind_doc},
{"draw_view3d",
- (PyCFunction)bpygpu_offscreen_draw_view3d,
+ (PyCFunction)py_offscreen_draw_view3d,
METH_VARARGS | METH_KEYWORDS,
- bpygpu_offscreen_draw_view3d_doc},
- {"free", (PyCFunction)bpygpu_offscreen_free, METH_NOARGS, bpygpu_offscreen_free_doc},
- {"__enter__", (PyCFunction)bpygpu_offscreen_bind_context_enter, METH_NOARGS},
- {"__exit__", (PyCFunction)bpygpu_offscreen_bind_context_exit, METH_VARARGS},
+ py_offscreen_draw_view3d_doc},
+ {"free", (PyCFunction)py_offscreen_free, METH_NOARGS, py_offscreen_free_doc},
+ {"__enter__", (PyCFunction)py_offscreen_bind_context_enter, METH_NOARGS},
+ {"__exit__", (PyCFunction)py_offscreen_bind_context_exit, METH_VARARGS},
{NULL, NULL, 0, NULL},
};
-PyDoc_STRVAR(bpygpu_offscreen_doc,
+PyDoc_STRVAR(py_offscreen_doc,
".. class:: GPUOffScreen(width, height)\n"
"\n"
" This object gives access to off screen buffers.\n"
@@ -353,10 +344,10 @@ PyTypeObject BPyGPUOffScreen_Type = {
.tp_basicsize = sizeof(BPyGPUOffScreen),
.tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = bpygpu_offscreen_doc,
- .tp_methods = bpygpu_offscreen_methods,
- .tp_getset = bpygpu_offscreen_getseters,
- .tp_new = bpygpu_offscreen_new,
+ .tp_doc = py_offscreen_doc,
+ .tp_methods = py_offscreen_methods,
+ .tp_getset = py_offscreen_getseters,
+ .tp_new = py_offscreen_new,
};
/** \} */
diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c
index abe46301abb..4fa9d5ebc7a 100644
--- a/source/blender/python/gpu/gpu_py_select.c
+++ b/source/blender/python/gpu/gpu_py_select.c
@@ -40,14 +40,14 @@
/** \name Methods
* \{ */
-PyDoc_STRVAR(bpygpu_select_load_id_doc,
+PyDoc_STRVAR(py_select_load_id_doc,
".. function:: load_id(id)\n"
"\n"
" Set the selection ID.\n"
"\n"
" :param id: Number (32-bit uint).\n"
" :type select: int\n");
-static PyObject *bpygpu_select_load_id(PyObject *UNUSED(self), PyObject *value)
+static PyObject *py_select_load_id(PyObject *UNUSED(self), PyObject *value)
{
uint id;
if ((id = PyC_Long_AsU32(value)) == (uint)-1) {
@@ -62,18 +62,18 @@ static PyObject *bpygpu_select_load_id(PyObject *UNUSED(self), PyObject *value)
/** \name Module
* \{ */
-static struct PyMethodDef bpygpu_select_methods[] = {
+static struct PyMethodDef py_select_methods[] = {
/* Manage Stack */
- {"load_id", (PyCFunction)bpygpu_select_load_id, METH_O, bpygpu_select_load_id_doc},
+ {"load_id", (PyCFunction)py_select_load_id, METH_O, py_select_load_id_doc},
{NULL, NULL, 0, NULL},
};
-PyDoc_STRVAR(bpygpu_select_doc, "This module provides access to selection.");
+PyDoc_STRVAR(py_select_doc, "This module provides access to selection.");
static PyModuleDef BPyGPU_select_module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "gpu.select",
- .m_doc = bpygpu_select_doc,
- .m_methods = bpygpu_select_methods,
+ .m_doc = py_select_doc,
+ .m_methods = py_select_methods,
};
PyObject *BPyInit_gpu_select(void)
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index c90a4a7bc9d..526b96f8584 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -39,45 +39,19 @@
/** \name Enum Conversion.
* \{ */
-static int bpygpu_ParseBultinShaderEnum(PyObject *o, void *p)
-{
- Py_ssize_t mode_id_len;
- const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
- if (mode_id == NULL) {
- PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
- return 0;
- }
-#define MATCH_ID(id) \
- if (mode_id_len == (Py_ssize_t)strlen(STRINGIFY(id))) { \
- if (STREQ(mode_id, STRINGIFY(id))) { \
- mode = GPU_SHADER_##id; \
- goto success; \
- } \
- } \
- ((void)0)
-
- eGPUBuiltinShader mode;
- MATCH_ID(2D_UNIFORM_COLOR);
- MATCH_ID(2D_FLAT_COLOR);
- MATCH_ID(2D_SMOOTH_COLOR);
- MATCH_ID(2D_IMAGE);
- MATCH_ID(3D_UNIFORM_COLOR);
- MATCH_ID(3D_FLAT_COLOR);
- MATCH_ID(3D_SMOOTH_COLOR);
- MATCH_ID(3D_POLYLINE_UNIFORM_COLOR);
-
-#undef MATCH_ID
- PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", mode_id);
- return 0;
-
-success:
- (*(eGPUBuiltinShader *)p) = mode;
- return 1;
-}
+static const struct PyC_StringEnumItems pygpu_bultinshader_items[] = {
+ {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"},
+ {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"},
+ {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"},
+ {GPU_SHADER_2D_IMAGE, "2D_IMAGE"},
+ {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"},
+ {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"},
+ {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"},
+ {0, NULL},
+};
-static int bpygpu_uniform_location_get(GPUShader *shader,
- const char *name,
- const char *error_prefix)
+static int py_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix)
{
const int uniform = GPU_shader_get_uniform(shader, name);
@@ -94,7 +68,7 @@ static int bpygpu_uniform_location_get(GPUShader *shader,
/** \name Shader Type
* \{ */
-static PyObject *bpygpu_shader_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
+static PyObject *py_shader_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
@@ -133,17 +107,17 @@ static PyObject *bpygpu_shader_new(PyTypeObject *UNUSED(type), PyObject *args, P
}
PyDoc_STRVAR(
- bpygpu_shader_bind_doc,
+ py_shader_bind_doc,
".. method:: bind()\n"
"\n"
" Bind the shader object. Required to be able to change uniforms of this shader.\n");
-static PyObject *bpygpu_shader_bind(BPyGPUShader *self)
+static PyObject *py_shader_bind(BPyGPUShader *self)
{
GPU_shader_bind(self->shader);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_from_name_doc,
+PyDoc_STRVAR(py_shader_uniform_from_name_doc,
".. method:: uniform_from_name(name)\n"
"\n"
" Get uniform location by name.\n"
@@ -152,14 +126,14 @@ PyDoc_STRVAR(bpygpu_shader_uniform_from_name_doc,
" :type name: `str`\n"
" :return: Location of the uniform variable.\n"
" :rtype: `int`\n");
-static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
+static PyObject *py_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg)
{
const char *name = PyUnicode_AsUTF8(arg);
if (name == NULL) {
return NULL;
}
- const int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform");
+ const int uniform = py_uniform_location_get(self->shader, name, "GPUShader.get_uniform");
if (uniform == -1) {
return NULL;
@@ -169,7 +143,7 @@ static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *a
}
PyDoc_STRVAR(
- bpygpu_shader_uniform_block_from_name_doc,
+ py_shader_uniform_block_from_name_doc,
".. method:: uniform_block_from_name(name)\n"
"\n"
" Get uniform block location by name.\n"
@@ -178,7 +152,7 @@ PyDoc_STRVAR(
" :type name: `str`\n"
" :return: The location of the uniform block variable.\n"
" :rtype: `int`\n");
-static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg)
+static PyObject *py_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg)
{
const char *name = PyUnicode_AsUTF8(arg);
if (name == NULL) {
@@ -195,12 +169,12 @@ static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObj
return PyLong_FromLong(uniform);
}
-static bool bpygpu_shader_uniform_vector_imp(PyObject *args,
- int elem_size,
- int *r_location,
- int *r_length,
- int *r_count,
- Py_buffer *r_pybuffer)
+static bool py_shader_uniform_vector_imp(PyObject *args,
+ int elem_size,
+ int *r_location,
+ int *r_length,
+ int *r_count,
+ Py_buffer *r_pybuffer)
{
PyObject *buffer;
@@ -223,7 +197,7 @@ static bool bpygpu_shader_uniform_vector_imp(PyObject *args,
return true;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_vector_float_doc,
+PyDoc_STRVAR(py_shader_uniform_vector_float_doc,
".. method:: uniform_vector_float(location, buffer, length, count)\n"
"\n"
" Set the buffer to fill the uniform.\n"
@@ -243,14 +217,13 @@ PyDoc_STRVAR(bpygpu_shader_uniform_vector_float_doc,
" :param count: Specifies the number of elements, vector or matrices that are to "
"be modified.\n"
" :type count: int\n");
-static PyObject *bpygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
+static PyObject *py_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
{
int location, length, count;
Py_buffer pybuffer;
- if (!bpygpu_shader_uniform_vector_imp(
- args, sizeof(float), &location, &length, &count, &pybuffer)) {
+ if (!py_shader_uniform_vector_imp(args, sizeof(float), &location, &length, &count, &pybuffer)) {
return NULL;
}
@@ -261,18 +234,17 @@ static PyObject *bpygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_vector_int_doc,
+PyDoc_STRVAR(py_shader_uniform_vector_int_doc,
".. method:: uniform_vector_int(location, buffer, length, count)\n"
"\n"
" See GPUShader.uniform_vector_float(...) description.\n");
-static PyObject *bpygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args)
+static PyObject *py_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args)
{
int location, length, count;
Py_buffer pybuffer;
- if (!bpygpu_shader_uniform_vector_imp(
- args, sizeof(int), &location, &length, &count, &pybuffer)) {
+ if (!py_shader_uniform_vector_imp(args, sizeof(int), &location, &length, &count, &pybuffer)) {
return NULL;
}
@@ -283,7 +255,7 @@ static PyObject *bpygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_bool_doc,
+PyDoc_STRVAR(py_shader_uniform_bool_doc,
".. method:: uniform_bool(name, seq)\n"
"\n"
" Specify the value of a uniform variable for the current program object.\n"
@@ -292,7 +264,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_bool_doc,
" :type name: str\n"
" :param seq: Value that will be used to update the specified uniform variable.\n"
" :type seq: sequence of bools\n");
-static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
+static PyObject *py_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
{
const char *error_prefix = "GPUShader.uniform_bool";
@@ -336,7 +308,7 @@ static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
return NULL;
}
- const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix);
+ const int location = py_uniform_location_get(self->shader, params.id, error_prefix);
if (location == -1) {
return NULL;
@@ -347,7 +319,7 @@ static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_float_doc,
+PyDoc_STRVAR(py_shader_uniform_float_doc,
".. method:: uniform_float(name, value)\n"
"\n"
" Specify the value of a uniform variable for the current program object.\n"
@@ -356,7 +328,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_float_doc,
" :type name: str\n"
" :param value: Value that will be used to update the specified uniform variable.\n"
" :type value: single number or sequence of numbers\n");
-static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
+static PyObject *py_shader_uniform_float(BPyGPUShader *self, PyObject *args)
{
const char *error_prefix = "GPUShader.uniform_float";
@@ -405,7 +377,7 @@ static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
return NULL;
}
- const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix);
+ const int location = py_uniform_location_get(self->shader, params.id, error_prefix);
if (location == -1) {
return NULL;
@@ -416,7 +388,7 @@ static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_uniform_int_doc,
+PyDoc_STRVAR(py_shader_uniform_int_doc,
".. method:: uniform_int(name, seq)\n"
"\n"
" Specify the value of a uniform variable for the current program object.\n"
@@ -425,7 +397,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_int_doc,
" :type name: str\n"
" :param seq: Value that will be used to update the specified uniform variable.\n"
" :type seq: sequence of numbers\n");
-static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
+static PyObject *py_shader_uniform_int(BPyGPUShader *self, PyObject *args)
{
const char *error_prefix = "GPUShader.uniform_int";
@@ -475,7 +447,7 @@ static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
return NULL;
}
- const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix);
+ const int location = py_uniform_location_get(self->shader, params.id, error_prefix);
if (location == -1) {
return NULL;
@@ -487,7 +459,7 @@ static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
}
PyDoc_STRVAR(
- bpygpu_shader_attr_from_name_doc,
+ py_shader_attr_from_name_doc,
".. method:: attr_from_name(name)\n"
"\n"
" Get attribute location by name.\n"
@@ -496,7 +468,7 @@ PyDoc_STRVAR(
" :type name: str\n"
" :return: The location of an attribute variable.\n"
" :rtype: int\n");
-static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
+static PyObject *py_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
{
const char *name = PyUnicode_AsUTF8(arg);
if (name == NULL) {
@@ -513,75 +485,69 @@ static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
return PyLong_FromLong(attr);
}
-PyDoc_STRVAR(bpygpu_shader_calc_format_doc,
+PyDoc_STRVAR(py_shader_calc_format_doc,
".. method:: calc_format()\n"
"\n"
" Build a new format based on the attributes of the shader.\n"
"\n"
" :return: vertex attribute format for the shader\n"
" :rtype: GPUVertFormat\n");
-static PyObject *bpygpu_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg))
+static PyObject *py_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg))
{
BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL);
GPU_vertformat_from_shader(&ret->fmt, self->shader);
return (PyObject *)ret;
}
-static struct PyMethodDef bpygpu_shader_methods[] = {
- {"bind", (PyCFunction)bpygpu_shader_bind, METH_NOARGS, bpygpu_shader_bind_doc},
+static struct PyMethodDef py_shader_methods[] = {
+ {"bind", (PyCFunction)py_shader_bind, METH_NOARGS, py_shader_bind_doc},
{"uniform_from_name",
- (PyCFunction)bpygpu_shader_uniform_from_name,
+ (PyCFunction)py_shader_uniform_from_name,
METH_O,
- bpygpu_shader_uniform_from_name_doc},
+ py_shader_uniform_from_name_doc},
{"uniform_block_from_name",
- (PyCFunction)bpygpu_shader_uniform_block_from_name,
+ (PyCFunction)py_shader_uniform_block_from_name,
METH_O,
- bpygpu_shader_uniform_block_from_name_doc},
+ py_shader_uniform_block_from_name_doc},
{"uniform_vector_float",
- (PyCFunction)bpygpu_shader_uniform_vector_float,
+ (PyCFunction)py_shader_uniform_vector_float,
METH_VARARGS,
- bpygpu_shader_uniform_vector_float_doc},
+ py_shader_uniform_vector_float_doc},
{"uniform_vector_int",
- (PyCFunction)bpygpu_shader_uniform_vector_int,
+ (PyCFunction)py_shader_uniform_vector_int,
METH_VARARGS,
- bpygpu_shader_uniform_vector_int_doc},
+ py_shader_uniform_vector_int_doc},
{"uniform_bool",
- (PyCFunction)bpygpu_shader_uniform_bool,
+ (PyCFunction)py_shader_uniform_bool,
METH_VARARGS,
- bpygpu_shader_uniform_bool_doc},
+ py_shader_uniform_bool_doc},
{"uniform_float",
- (PyCFunction)bpygpu_shader_uniform_float,
- METH_VARARGS,
- bpygpu_shader_uniform_float_doc},
- {"uniform_int",
- (PyCFunction)bpygpu_shader_uniform_int,
+ (PyCFunction)py_shader_uniform_float,
METH_VARARGS,
- bpygpu_shader_uniform_int_doc},
+ py_shader_uniform_float_doc},
+ {"uniform_int", (PyCFunction)py_shader_uniform_int, METH_VARARGS, py_shader_uniform_int_doc},
{"attr_from_name",
- (PyCFunction)bpygpu_shader_attr_from_name,
+ (PyCFunction)py_shader_attr_from_name,
METH_O,
- bpygpu_shader_attr_from_name_doc},
- {"format_calc",
- (PyCFunction)bpygpu_shader_calc_format,
- METH_NOARGS,
- bpygpu_shader_calc_format_doc},
+ py_shader_attr_from_name_doc},
+ {"format_calc", (PyCFunction)py_shader_calc_format, METH_NOARGS, py_shader_calc_format_doc},
{NULL, NULL, 0, NULL},
};
PyDoc_STRVAR(
- bpygpu_shader_program_doc,
+ py_shader_program_doc,
"The name of the program object for use by the OpenGL API (read-only).\n\n:type: int");
-static PyObject *bpygpu_shader_program_get(BPyGPUShader *self, void *UNUSED(closure))
+static PyObject *py_shader_program_get(BPyGPUShader *self, void *UNUSED(closure))
{
return PyLong_FromLong(GPU_shader_get_program(self->shader));
}
-static PyGetSetDef bpygpu_shader_getseters[] = {
- {"program", (getter)bpygpu_shader_program_get, (setter)NULL, bpygpu_shader_program_doc, NULL},
+static PyGetSetDef py_shader_getseters[] = {
+ {"program", (getter)py_shader_program_get, (setter)NULL, py_shader_program_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};
-static void bpygpu_shader_dealloc(BPyGPUShader *self)
+static void py_shader_dealloc(BPyGPUShader *self)
{
if (self->is_builtin == false) {
GPU_shader_free(self->shader);
@@ -590,7 +556,7 @@ static void bpygpu_shader_dealloc(BPyGPUShader *self)
}
PyDoc_STRVAR(
- bpygpu_shader_doc,
+ py_shader_doc,
".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None)\n"
"\n"
" GPUShader combines multiple GLSL shaders into a program used for drawing.\n"
@@ -624,12 +590,12 @@ PyDoc_STRVAR(
PyTypeObject BPyGPUShader_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader",
.tp_basicsize = sizeof(BPyGPUShader),
- .tp_dealloc = (destructor)bpygpu_shader_dealloc,
+ .tp_dealloc = (destructor)py_shader_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = bpygpu_shader_doc,
- .tp_methods = bpygpu_shader_methods,
- .tp_getset = bpygpu_shader_getseters,
- .tp_new = bpygpu_shader_new,
+ .tp_doc = py_shader_doc,
+ .tp_methods = py_shader_methods,
+ .tp_getset = py_shader_getseters,
+ .tp_new = py_shader_new,
};
/** \} */
@@ -638,17 +604,17 @@ PyTypeObject BPyGPUShader_Type = {
/** \name gpu.shader Module API
* \{ */
-PyDoc_STRVAR(bpygpu_shader_unbind_doc,
+PyDoc_STRVAR(py_shader_unbind_doc,
".. function:: unbind()\n"
"\n"
" Unbind the bound shader object.\n");
-static PyObject *bpygpu_shader_unbind(BPyGPUShader *UNUSED(self))
+static PyObject *py_shader_unbind(BPyGPUShader *UNUSED(self))
{
GPU_shader_unbind();
Py_RETURN_NONE;
}
-PyDoc_STRVAR(bpygpu_shader_from_builtin_doc,
+PyDoc_STRVAR(py_shader_from_builtin_doc,
".. function:: from_builtin(shader_name)\n"
"\n"
" Shaders that are embedded in the blender internal code.\n"
@@ -668,22 +634,21 @@ PyDoc_STRVAR(bpygpu_shader_from_builtin_doc,
" :type shader_name: str\n"
" :return: Shader object corresponding to the given name.\n"
" :rtype: :class:`bpy.types.GPUShader`\n");
-static PyObject *bpygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg)
+static PyObject *py_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg)
{
BPYGPU_IS_INIT_OR_ERROR_OBJ;
- eGPUBuiltinShader shader_id;
-
- if (!bpygpu_ParseBultinShaderEnum(arg, &shader_id)) {
+ struct PyC_StringEnum pygpu_bultinshader = {pygpu_bultinshader_items};
+ if (!PyC_ParseStringEnum(arg, &pygpu_bultinshader)) {
return NULL;
}
- GPUShader *shader = GPU_shader_get_builtin_shader(shader_id);
+ GPUShader *shader = GPU_shader_get_builtin_shader(pygpu_bultinshader.value_found);
return BPyGPUShader_CreatePyObject(shader, true);
}
-PyDoc_STRVAR(bpygpu_shader_code_from_builtin_doc,
+PyDoc_STRVAR(py_shader_code_from_builtin_doc,
".. function:: code_from_builtin(shader_name)\n"
"\n"
" Exposes the internal shader code for query.\n"
@@ -699,10 +664,8 @@ PyDoc_STRVAR(bpygpu_shader_code_from_builtin_doc,
" :type shader_name: str\n"
" :return: Vertex, fragment and geometry shader codes.\n"
" :rtype: dict\n");
-static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg)
+static PyObject *py_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg)
{
- eGPUBuiltinShader shader_id;
-
const char *vert;
const char *frag;
const char *geom;
@@ -710,11 +673,13 @@ static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyO
PyObject *item, *r_dict;
- if (!bpygpu_ParseBultinShaderEnum(arg, &shader_id)) {
+ struct PyC_StringEnum pygpu_bultinshader = {pygpu_bultinshader_items};
+ if (!PyC_ParseStringEnum(arg, &pygpu_bultinshader)) {
return NULL;
}
- GPU_shader_get_builtin_shader_code(shader_id, &vert, &frag, &geom, &defines);
+ GPU_shader_get_builtin_shader_code(
+ pygpu_bultinshader.value_found, &vert, &frag, &geom, &defines);
r_dict = PyDict_New();
@@ -735,20 +700,17 @@ static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyO
return r_dict;
}
-static struct PyMethodDef bpygpu_shader_module_methods[] = {
- {"unbind", (PyCFunction)bpygpu_shader_unbind, METH_NOARGS, bpygpu_shader_unbind_doc},
- {"from_builtin",
- (PyCFunction)bpygpu_shader_from_builtin,
- METH_O,
- bpygpu_shader_from_builtin_doc},
+static struct PyMethodDef py_shader_module_methods[] = {
+ {"unbind", (PyCFunction)py_shader_unbind, METH_NOARGS, py_shader_unbind_doc},
+ {"from_builtin", (PyCFunction)py_shader_from_builtin, METH_O, py_shader_from_builtin_doc},
{"code_from_builtin",
- (PyCFunction)bpygpu_shader_code_from_builtin,
+ (PyCFunction)py_shader_code_from_builtin,
METH_O,
- bpygpu_shader_code_from_builtin_doc},
+ py_shader_code_from_builtin_doc},
{NULL, NULL, 0, NULL},
};
-PyDoc_STRVAR(bpygpu_shader_module_doc,
+PyDoc_STRVAR(py_shader_module_doc,
"This module provides access to GPUShader internal functions.\n"
"\n"
".. rubric:: Built-in shaders\n"
@@ -780,8 +742,8 @@ PyDoc_STRVAR(bpygpu_shader_module_doc,
static PyModuleDef BPyGPU_shader_module_def = {
PyModuleDef_HEAD_INIT,
.m_name = "gpu.shader",
- .m_doc = bpygpu_shader_module_doc,
- .m_methods = bpygpu_shader_module_methods,
+ .m_doc = py_shader_module_doc,
+ .m_methods = py_shader_module_methods,
};
/** \} */
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c
index d8c6e67816a..8e19eac76d0 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.c
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c
@@ -116,10 +116,10 @@ static void fill_format_sequence(void *data_dst_void,
#undef WARN_TYPE_LIMIT_PUSH
#undef WARN_TYPE_LIMIT_POP
-static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo,
- uint data_id,
- PyObject *seq,
- const char *error_prefix)
+static bool py_vertbuf_fill_impl(GPUVertBuf *vbo,
+ uint data_id,
+ PyObject *seq,
+ const char *error_prefix)
{
const char *exc_str_size_mismatch = "Expected a %s of size %d, got %u";
@@ -213,10 +213,7 @@ static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo,
return ok;
}
-static int bpygpu_attr_fill(GPUVertBuf *buf,
- int id,
- PyObject *py_seq_data,
- const char *error_prefix)
+static int py_attr_fill(GPUVertBuf *buf, int id, PyObject *py_seq_data, const char *error_prefix)
{
if (id < 0 || id >= GPU_vertbuf_get_format(buf)->attr_len) {
PyErr_Format(PyExc_ValueError, "Format id %d out of range", id);
@@ -228,7 +225,7 @@ static int bpygpu_attr_fill(GPUVertBuf *buf,
return 0;
}
- if (!bpygpu_vertbuf_fill_impl(buf, (uint)id, py_seq_data, error_prefix)) {
+ if (!py_vertbuf_fill_impl(buf, (uint)id, py_seq_data, error_prefix)) {
return 0;
}
@@ -241,7 +238,7 @@ static int bpygpu_attr_fill(GPUVertBuf *buf,
/** \name VertBuf Type
* \{ */
-static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
+static PyObject *py_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
struct {
PyObject *py_fmt;
@@ -263,7 +260,7 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args,
return BPyGPUVertBuf_CreatePyObject(vbo);
}
-PyDoc_STRVAR(bpygpu_VertBuf_attr_fill_doc,
+PyDoc_STRVAR(py_VertBuf_attr_fill_doc,
".. method:: attr_fill(id, data)\n"
"\n"
" Insert data into the buffer for a single attribute.\n"
@@ -272,7 +269,7 @@ PyDoc_STRVAR(bpygpu_VertBuf_attr_fill_doc,
" :type id: int or str\n"
" :param data: Sequence of data that should be stored in the buffer\n"
" :type data: sequence of values or tuples\n");
-static PyObject *bpygpu_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
+static PyObject *py_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
{
PyObject *data;
PyObject *identifier;
@@ -302,22 +299,22 @@ static PyObject *bpygpu_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, P
return NULL;
}
- if (!bpygpu_attr_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
+ if (!py_attr_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
return NULL;
}
Py_RETURN_NONE;
}
-static struct PyMethodDef bpygpu_VertBuf_methods[] = {
+static struct PyMethodDef py_VertBuf_methods[] = {
{"attr_fill",
- (PyCFunction)bpygpu_VertBuf_attr_fill,
+ (PyCFunction)py_VertBuf_attr_fill,
METH_VARARGS | METH_KEYWORDS,
- bpygpu_VertBuf_attr_fill_doc},
+ py_VertBuf_attr_fill_doc},
{NULL, NULL, 0, NULL},
};
-static void bpygpu_VertBuf_dealloc(BPyGPUVertBuf *self)
+static void py_VertBuf_dealloc(BPyGPUVertBuf *self)
{
GPU_vertbuf_discard(self->buf);
Py_TYPE(self)->tp_free(self);
@@ -335,11 +332,11 @@ PyDoc_STRVAR(py_gpu_vertex_buffer_doc,
PyTypeObject BPyGPUVertBuf_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf",
.tp_basicsize = sizeof(BPyGPUVertBuf),
- .tp_dealloc = (destructor)bpygpu_VertBuf_dealloc,
+ .tp_dealloc = (destructor)py_VertBuf_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = py_gpu_vertex_buffer_doc,
- .tp_methods = bpygpu_VertBuf_methods,
- .tp_new = bpygpu_VertBuf_new,
+ .tp_methods = py_VertBuf_methods,
+ .tp_new = py_VertBuf_new,
};
/** \} */
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c
index 2d45eadcfe3..7d1e4ee4868 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.c
+++ b/source/blender/python/gpu/gpu_py_vertex_format.c
@@ -50,7 +50,7 @@
* Use with PyArg_ParseTuple's "O&" formatting.
* \{ */
-static int bpygpu_parse_component_type(const char *str, int length)
+static int py_parse_component_type(const char *str, int length)
{
if (length == 2) {
switch (*((ushort *)str)) {
@@ -83,7 +83,7 @@ static int bpygpu_parse_component_type(const char *str, int length)
return -1;
}
-static int bpygpu_parse_fetch_mode(const char *str, int length)
+static int py_parse_fetch_mode(const char *str, int length)
{
#define MATCH_ID(id) \
if (length == strlen(STRINGIFY(id))) { \
@@ -102,7 +102,7 @@ static int bpygpu_parse_fetch_mode(const char *str, int length)
return -1;
}
-static int bpygpu_ParseVertCompType(PyObject *o, void *p)
+static int py_ParseVertCompType(PyObject *o, void *p)
{
Py_ssize_t length;
const char *str = _PyUnicode_AsStringAndSize(o, &length);
@@ -112,7 +112,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p)
return 0;
}
- const int comp_type = bpygpu_parse_component_type(str, length);
+ const int comp_type = py_parse_component_type(str, length);
if (comp_type == -1) {
PyErr_Format(PyExc_ValueError, "unknown component type: '%s", str);
return 0;
@@ -122,7 +122,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p)
return 1;
}
-static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
+static int py_ParseVertFetchMode(PyObject *o, void *p)
{
Py_ssize_t length;
const char *str = _PyUnicode_AsStringAndSize(o, &length);
@@ -132,7 +132,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
return 0;
}
- const int fetch_mode = bpygpu_parse_fetch_mode(str, length);
+ const int fetch_mode = py_parse_fetch_mode(str, length);
if (fetch_mode == -1) {
PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str);
return 0;
@@ -148,7 +148,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
/** \name VertFormat Type
* \{ */
-static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
+static PyObject *py_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
PyErr_SetString(PyExc_ValueError, "This function takes no arguments");
@@ -158,7 +158,7 @@ static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *arg
}
PyDoc_STRVAR(
- bpygpu_VertFormat_attr_add_doc,
+ py_VertFormat_attr_add_doc,
".. method:: attr_add(id, comp_type, len, fetch_mode)\n"
"\n"
" Add a new attribute to the format.\n"
@@ -177,7 +177,7 @@ PyDoc_STRVAR(
" converted to a normal 4 byte float when used.\n"
" Possible values are `FLOAT`, `INT`, `INT_TO_FLOAT_UNIT` and `INT_TO_FLOAT`.\n"
" :type fetch_mode: `str`\n");
-static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds)
+static PyObject *py_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds)
{
struct {
const char *id;
@@ -197,10 +197,10 @@ static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *ar
kwds,
&_parser,
&params.id,
- bpygpu_ParseVertCompType,
+ py_ParseVertCompType,
&params.comp_type,
&params.len,
- bpygpu_ParseVertFetchMode,
+ py_ParseVertFetchMode,
&params.fetch_mode)) {
return NULL;
}
@@ -210,31 +210,31 @@ static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *ar
return PyLong_FromLong(attr_id);
}
-static struct PyMethodDef bpygpu_VertFormat_methods[] = {
+static struct PyMethodDef py_VertFormat_methods[] = {
{"attr_add",
- (PyCFunction)bpygpu_VertFormat_attr_add,
+ (PyCFunction)py_VertFormat_attr_add,
METH_VARARGS | METH_KEYWORDS,
- bpygpu_VertFormat_attr_add_doc},
+ py_VertFormat_attr_add_doc},
{NULL, NULL, 0, NULL},
};
-static void bpygpu_VertFormat_dealloc(BPyGPUVertFormat *self)
+static void py_VertFormat_dealloc(BPyGPUVertFormat *self)
{
Py_TYPE(self)->tp_free(self);
}
-PyDoc_STRVAR(bpygpu_VertFormat_doc,
+PyDoc_STRVAR(py_VertFormat_doc,
".. class:: GPUVertFormat()\n"
"\n"
" This object contains information about the structure of a vertex buffer.\n");
PyTypeObject BPyGPUVertFormat_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertFormat",
.tp_basicsize = sizeof(BPyGPUVertFormat),
- .tp_dealloc = (destructor)bpygpu_VertFormat_dealloc,
+ .tp_dealloc = (destructor)py_VertFormat_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = bpygpu_VertFormat_doc,
- .tp_methods = bpygpu_VertFormat_methods,
- .tp_new = bpygpu_VertFormat_new,
+ .tp_doc = py_VertFormat_doc,
+ .tp_methods = py_VertFormat_methods,
+ .tp_new = py_VertFormat_new,
};
/** \} */
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 9d69d91c8c8..354086ef4c3 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -43,6 +43,8 @@
#include "MEM_guardedalloc.h"
+#include "DNA_ID.h" /* MAX_IDPROP_NAME */
+
#include "../generic/py_capi_utils.h"
/* initial definition of callback slots we'll probably have more than 1 */
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index d1f69ddfc02..0046474d064 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -60,9 +60,9 @@ set(SRC
RE_texture.h
intern/initrender.h
+ intern/pipeline.h
intern/render_result.h
intern/render_types.h
- intern/pipeline.h
intern/texture_common.h
intern/zbuf.h
)
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 4dd2b300700..3e73ac77fc6 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -114,7 +114,7 @@ typedef struct RenderResult {
/* target image size */
int rectx, recty;
- short crop, sample_nr;
+ short sample_nr;
/* The following rect32, rectf and rectz buffers are for temporary storage only,
* for RenderResult structs created in #RE_AcquireResultImage - which do not have RenderView */
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index 769077c0e8c..5685911c42e 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -288,7 +288,7 @@ RenderResult *RE_engine_begin_result(
disprect.ymin = y;
disprect.ymax = y + h;
- result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname);
+ result = render_result_new(re, &disprect, RR_USE_MEM, layername, viewname);
/* todo: make this thread safe */
@@ -846,7 +846,7 @@ int RE_engine_render(Render *re, int do_all)
if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) {
savebuffers = RR_USE_EXR;
}
- re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ re->result = render_result_new(re, &re->disprect, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS);
}
BLI_rw_mutex_unlock(&re->resultmutex);
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 3d19e5e6c15..6b55b82ac97 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -905,7 +905,7 @@ static void render_result_rescale(Render *re)
if (src_rectf != NULL) {
float *dst_rectf = NULL;
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, "");
+ re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, "");
if (re->result != NULL) {
dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf;
@@ -1162,7 +1162,7 @@ static void render_result_uncrop(Render *re)
/* weak is: it chances disprect from border */
render_result_disprect_to_full_resolution(re);
- rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data);
render_result_clone_passes(re, rres, NULL);
@@ -1358,7 +1358,7 @@ static void do_render_composite(Render *re)
if ((re->r.mode & R_CROP) == 0) {
render_result_disprect_to_full_resolution(re);
}
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
BLI_rw_mutex_unlock(&re->resultmutex);
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index dfce51ec3ab..1ed894751ce 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -285,12 +285,8 @@ RenderPass *render_layer_add_pass(RenderResult *rr,
/* will read info from Render *re to define layers */
/* called in threads */
/* re->winx,winy is coordinate space of entire image, partrct the part within */
-RenderResult *render_result_new(Render *re,
- rcti *partrct,
- int crop,
- int savebuffers,
- const char *layername,
- const char *viewname)
+RenderResult *render_result_new(
+ Render *re, rcti *partrct, int savebuffers, const char *layername, const char *viewname)
{
RenderResult *rr;
RenderLayer *rl;
@@ -308,9 +304,7 @@ RenderResult *render_result_new(Render *re,
rr->rectx = rectx;
rr->recty = recty;
rr->renrect.xmin = 0;
- rr->renrect.xmax = rectx - 2 * crop;
- /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */
- rr->crop = crop;
+ rr->renrect.xmax = rectx;
/* tilerect is relative coordinates within render disprect. do not subtract crop yet */
rr->tilerect.xmin = partrct->xmin - re->disprect.xmin;
@@ -827,20 +821,8 @@ static void do_merge_tile(
copylen = tilex = rrpart->rectx;
tiley = rrpart->recty;
- if (rrpart->crop) { /* filters add pixel extra */
- tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex);
-
- copylen = tilex - 2 * rrpart->crop;
- tiley -= 2 * rrpart->crop;
-
- ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx +
- (rrpart->tilerect.xmin + rrpart->crop);
- target += pixsize * ofs;
- }
- else {
- ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin);
- target += pixsize * ofs;
- }
+ ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin);
+ target += pixsize * ofs;
copylen *= sizeof(float) * pixsize;
tilex *= pixsize;
@@ -1107,7 +1089,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons
{
RenderLayer *rlp, *rl;
RenderPass *rpassp;
- int offs, partx, party;
+ int partx, party;
BLI_thread_lock(LOCK_IMAGE);
@@ -1120,13 +1102,6 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons
continue;
}
- if (rrpart->crop) { /* filters add pixel extra */
- offs = (rrpart->crop + rrpart->crop * rrpart->rectx);
- }
- else {
- offs = 0;
- }
-
/* passes are allocated in sync */
for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) {
const int xstride = rpassp->channels;
@@ -1141,13 +1116,13 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons
fullname,
xstride,
xstride * rrpart->rectx,
- rpassp->rect + a + xstride * offs);
+ rpassp->rect + a);
}
}
}
- party = rrpart->tilerect.ymin + rrpart->crop;
- partx = rrpart->tilerect.xmin + rrpart->crop;
+ party = rrpart->tilerect.ymin;
+ partx = rrpart->tilerect.xmin;
for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) {
rl = RE_GetRenderLayer(rr, rlp->name);
@@ -1267,7 +1242,7 @@ void render_result_exr_file_end(Render *re, RenderEngine *engine)
/* Create new render result in memory instead of on disk. */
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
render_result_free_list(&re->fullresult, re->result);
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
BLI_rw_mutex_unlock(&re->resultmutex);
LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) {
@@ -1429,7 +1404,7 @@ bool render_result_exr_file_cache_read(Render *re)
char *root = U.render_cachedir;
RE_FreeRenderResult(re->result);
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
+ re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
/* First try cache. */
render_result_exr_file_cache_path(re->scene, root, str);
diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h
index 5c487223e94..67edd075e24 100644
--- a/source/blender/render/intern/render_result.h
+++ b/source/blender/render/intern/render_result.h
@@ -51,7 +51,6 @@ extern "C" {
struct RenderResult *render_result_new(struct Render *re,
struct rcti *partrct,
- int crop,
int savebuffers,
const char *layername,
const char *viewname);
diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c
index e2568e0a013..4254f6b0aa6 100644
--- a/source/blender/render/intern/texture_pointdensity.c
+++ b/source/blender/render/intern/texture_pointdensity.c
@@ -44,6 +44,7 @@
#include "BKE_colorband.h"
#include "BKE_colortools.h"
+#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_lattice.h"
#include "BKE_object.h"
diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt
index 18755e7e6bc..db46fe91718 100644
--- a/source/blender/sequencer/CMakeLists.txt
+++ b/source/blender/sequencer/CMakeLists.txt
@@ -21,6 +21,7 @@
set(INC
.
intern
+ ../blenfont
../blenkernel
../blenlib
../blenloader
@@ -30,7 +31,6 @@ set(INC
../makesdna
../makesrna
../render
- ../blenfont
../windowmanager
../../../intern/atomic
../../../intern/guardedalloc
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 11e213d842d..3a9c23de5cc 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -23,10 +23,15 @@
* \ingroup sequencer
*/
+#include "DNA_scene_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+struct BlendDataReader;
+struct BlendLibReader;
+struct BlendWriter;
struct Depsgraph;
struct Editing;
struct GPUOffScreen;
@@ -47,6 +52,7 @@ struct bSound;
struct BlendWriter;
struct BlendDataReader;
struct BlendLibReader;
+struct SequencerToolSettings;
/* Wipe effect */
enum {
@@ -161,7 +167,7 @@ void SEQ_render_new_render_data(struct Main *bmain,
int preview_render_size,
int for_render,
SeqRenderData *r_context);
-int SEQ_render_evaluate_frame(struct Scene *scene, int timeline_frame);
+int SEQ_render_evaluate_frame(struct ListBase *seqbase, int timeline_frame);
struct StripElem *SEQ_render_give_stripelem(struct Sequence *seq, int timeline_frame);
/* **********************************************************************
@@ -179,9 +185,16 @@ void SEQ_render_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4
* Sequencer scene functions
* ********************************************************************** */
+struct SequencerToolSettings *SEQ_tool_settings_init(void);
+void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings);
+eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene);
+void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method);
+
+struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings);
struct Editing *BKE_sequencer_editing_get(struct Scene *scene, bool alloc);
struct Editing *BKE_sequencer_editing_ensure(struct Scene *scene);
void BKE_sequencer_editing_free(struct Scene *scene, const bool do_id_user);
+struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed);
void BKE_sequencer_sort(struct Scene *scene);
struct Sequence *BKE_sequencer_from_elem(ListBase *seqbase, struct StripElem *se);
struct Sequence *BKE_sequencer_active_get(struct Scene *scene);
@@ -361,6 +374,20 @@ void BKE_sequence_invalidate_cache_in_range(struct Scene *scene,
void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int timeline_frame);
/* **********************************************************************
+ * util.c
+ *
+ * Add strips
+ * **********************************************************************
+ */
+
+void SEQ_set_scale_to_fit(const struct Sequence *seq,
+ const int image_width,
+ const int image_height,
+ const int preview_width,
+ const int preview_height,
+ const eSeqImageFitMethod fit_method);
+
+/* **********************************************************************
* sequencer.c
*
* Add strips
@@ -376,6 +403,7 @@ typedef struct SeqLoadInfo {
int type;
int len; /* only for image strips */
char path[1024]; /* 1024 = FILE_MAX */
+ eSeqImageFitMethod fit_method;
/* multiview */
char views_format;
@@ -595,6 +623,33 @@ struct Sequence *SEQ_edit_strip_split(struct Main *bmain,
struct Sequence *seq,
const int timeline_frame,
const eSeqSplitMethod method);
+bool SEQ_edit_remove_gaps(struct Scene *scene,
+ struct ListBase *seqbase,
+ const int initial_frame,
+ const bool remove_all_gaps);
+
+/* **********************************************************************
+ * strip_time.c
+ *
+ * Editing functions
+ * **********************************************************************
+ */
+
+void SEQ_timeline_boundbox(const struct Scene *scene,
+ const struct ListBase *seqbase,
+ struct rctf *rect);
+
+/* **********************************************************************
+ * strip_transform.c
+ *
+ * Editing functions
+ * **********************************************************************
+ */
+
+void SEQ_offset_after_frame(struct Scene *scene,
+ struct ListBase *seqbase,
+ const int delta,
+ const int timeline_frame);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index ba16206ce97..8ffbc453517 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -42,6 +42,7 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
+#include "DNA_vfont_types.h"
#include "BKE_fcurve.h"
#include "BKE_lib_id.h"
diff --git a/source/blender/sequencer/intern/effects.h b/source/blender/sequencer/intern/effects.h
index 58e0a97d4c5..6a94c0ea9d9 100644
--- a/source/blender/sequencer/intern/effects.h
+++ b/source/blender/sequencer/intern/effects.h
@@ -28,8 +28,8 @@ extern "C" {
#endif
struct Scene;
-struct Sequence;
struct SeqRenderData;
+struct Sequence;
/* **********************************************************************
* sequencer.c
diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h
index 0fcf0548628..2cb35670a2c 100644
--- a/source/blender/sequencer/intern/image_cache.h
+++ b/source/blender/sequencer/intern/image_cache.h
@@ -30,8 +30,8 @@ extern "C" {
struct ImBuf;
struct Main;
struct Scene;
-struct Sequence;
struct SeqRenderData;
+struct Sequence;
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/multiview.h b/source/blender/sequencer/intern/multiview.h
index e1f998d18e2..bbc66c6f84c 100644
--- a/source/blender/sequencer/intern/multiview.h
+++ b/source/blender/sequencer/intern/multiview.h
@@ -27,14 +27,7 @@
extern "C" {
#endif
-struct Editing;
-struct ImBuf;
-struct Main;
-struct Mask;
struct Scene;
-struct Sequence;
-struct StripColorBalance;
-struct StripElem;
/* **********************************************************************
* sequencer.c
diff --git a/source/blender/sequencer/intern/prefetch.h b/source/blender/sequencer/intern/prefetch.h
index aa47a9dbcfc..1633ee297f8 100644
--- a/source/blender/sequencer/intern/prefetch.h
+++ b/source/blender/sequencer/intern/prefetch.h
@@ -27,11 +27,9 @@
extern "C" {
#endif
-struct ImBuf;
-struct Main;
struct Scene;
-struct Sequence;
struct SeqRenderData;
+struct Sequence;
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/proxy.h b/source/blender/sequencer/intern/proxy.h
index a362a318a5a..a65fdcd42fe 100644
--- a/source/blender/sequencer/intern/proxy.h
+++ b/source/blender/sequencer/intern/proxy.h
@@ -27,10 +27,10 @@
extern "C" {
#endif
-struct anim;
struct ImBuf;
struct SeqRenderData;
struct Sequence;
+struct anim;
#define PROXY_MAXFILE (2 * FILE_MAXDIR + FILE_MAXFILE)
struct ImBuf *seq_proxy_fetch(const struct SeqRenderData *context,
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 008ea1cd3a0..2e757a06751 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -337,16 +337,17 @@ static int evaluate_seq_frame_gen(Sequence **seq_arr,
return totseq;
}
-int SEQ_render_evaluate_frame(Scene *scene, int timeline_frame)
+/**
+ * Count number of strips in timeline at timeline_frame
+ *
+ * \param seqbase: ListBase in which strips are located
+ * \param timeline_frame: frame on timeline from where gaps are searched for
+ * \return number of strips
+ */
+int SEQ_render_evaluate_frame(ListBase *seqbase, int timeline_frame)
{
- Editing *ed = BKE_sequencer_editing_get(scene, false);
Sequence *seq_arr[MAXSEQ + 1];
-
- if (ed == NULL) {
- return 0;
- }
-
- return evaluate_seq_frame_gen(seq_arr, ed->seqbasep, timeline_frame, 0);
+ return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0);
}
static bool video_seq_is_rendered(Sequence *seq)
@@ -527,7 +528,6 @@ static void sequencer_image_transform_init(void *handle_v,
handle->ibuf_source = init_data->ibuf_source;
handle->ibuf_out = init_data->ibuf_out;
handle->transform = init_data->transform;
- handle->scale_to_fit = init_data->scale_to_fit;
handle->image_scale_factor = init_data->image_scale_factor;
handle->for_render = init_data->for_render;
@@ -539,8 +539,8 @@ static void *sequencer_image_transform_do_thread(void *data_v)
{
const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v;
const StripTransform *transform = data->transform;
- const float scale_x = transform->scale_x * data->scale_to_fit;
- const float scale_y = transform->scale_y * data->scale_to_fit;
+ const float scale_x = transform->scale_x * data->image_scale_factor;
+ const float scale_y = transform->scale_y * data->image_scale_factor;
const float scale_to_fit_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2;
const float scale_to_fit_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2;
const float translate_x = transform->xofs * data->image_scale_factor + scale_to_fit_offs_x;
@@ -625,10 +625,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
IMB_filtery(preprocessed_ibuf);
}
- /* Calculate scale factor, so image fits in preview area with original aspect ratio. */
- const float scale_to_fit_factor = MIN2((float)context->rectx / (float)ibuf->x,
- (float)context->recty / (float)ibuf->y);
-
/* Get scale factor if preview resolution doesn't match project resolution. */
float preview_scale_factor;
if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
@@ -647,10 +643,10 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
const int height = ibuf->y;
const StripCrop *c = seq->strip->crop;
- const int left = c->left / scale_to_fit_factor * preview_scale_factor;
- const int right = c->right / scale_to_fit_factor * preview_scale_factor;
- const int top = c->top / scale_to_fit_factor * preview_scale_factor;
- const int bottom = c->bottom / scale_to_fit_factor * preview_scale_factor;
+ const int left = c->left;
+ const int right = c->right;
+ const int top = c->top;
+ const int bottom = c->bottom;
const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Left. */
@@ -672,7 +668,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
init_data.ibuf_source = ibuf;
init_data.ibuf_out = preprocessed_ibuf;
init_data.transform = seq->strip->transform;
- init_data.scale_to_fit = scale_to_fit_factor;
init_data.image_scale_factor = preview_scale_factor;
init_data.for_render = context->for_render;
IMB_processor_apply_threaded(context->recty,
diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h
index d5affeb547b..46748415187 100644
--- a/source/blender/sequencer/intern/render.h
+++ b/source/blender/sequencer/intern/render.h
@@ -27,7 +27,6 @@
extern "C" {
#endif
-struct Editing;
struct ImBuf;
struct ListBase;
struct Scene;
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index c998886626c..87b608ef141 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -301,6 +301,46 @@ static void seq_new_fix_links_recursive(Sequence *seq)
}
}
}
+
+SequencerToolSettings *SEQ_tool_settings_init(void)
+{
+ SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings),
+ "Sequencer tool settings");
+ tool_settings->fit_method = SEQ_SCALE_TO_FIT;
+ return tool_settings;
+}
+
+void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
+{
+ MEM_freeN(tool_settings);
+}
+
+eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
+{
+ const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ return tool_settings->fit_method;
+}
+
+void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method)
+{
+ SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ tool_settings->fit_method = fit_method;
+}
+
+/**
+ * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase
+ *
+ * \param ed: sequence editor data
+ * \return pointer to active seqbase. returns NULL if ed is NULL
+ */
+ListBase *SEQ_active_seqbase_get(const Editing *ed)
+{
+ if (ed == NULL) {
+ return NULL;
+ }
+
+ return ed->seqbasep;
+}
/** \} */
/* -------------------------------------------------------------------- */
@@ -609,4 +649,11 @@ static void seq_free_animdata(Scene *scene, Sequence *seq)
}
#undef SEQ_RNAPATH_MAXSTR
+
+SequencerToolSettings *SEQ_tool_settings_copy(SequencerToolSettings *tool_settings)
+{
+ SequencerToolSettings *tool_settings_copy = MEM_dupallocN(tool_settings);
+ return tool_settings_copy;
+}
+
/** \} */
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index d2e4025bdfc..e56dcf888a7 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -118,6 +118,16 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad
seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
seq_load_apply(CTX_data_main(C), scene, seq, seq_load);
+
+ char file_path[FILE_MAX];
+ BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name);
+ BLI_path_abs(file_path, BKE_main_blendfile_path(CTX_data_main(C)));
+ ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
+ if (ibuf != NULL) {
+ SEQ_set_scale_to_fit(seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
+ IMB_freeImBuf(ibuf);
+ }
+
BKE_sequence_invalidate_cache_composite(scene, seq);
return seq;
@@ -275,6 +285,11 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad
IMB_anim_load_metadata(anim_arr[0]);
seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
+
+ const float width = IMB_anim_get_image_width(anim_arr[0]);
+ const float height = IMB_anim_get_image_height(anim_arr[0]);
+ SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
+
BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2);
BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 3137a471470..a29810cc9ee 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -38,6 +38,8 @@
#include "BKE_scene.h"
#include "BKE_sound.h"
+#include "strip_time.h"
+
#include "SEQ_sequencer.h"
int BKE_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_str)
@@ -319,3 +321,36 @@ Sequence *SEQ_edit_strip_split(Main *bmain,
BKE_sequence_calc(scene, right_seq);
return right_seq;
}
+
+/**
+ * Find gap after initial_frame and move strips on right side to close the gap
+ *
+ * \param scene: Scene in which strips are located
+ * \param seqbase: ListBase in which strips are located
+ * \param initial_frame: frame on timeline from where gaps are searched for
+ * \param remove_all_gaps: remove all gaps instead of one gap
+ * \return true if gap is removed, otherwise false
+ */
+bool SEQ_edit_remove_gaps(Scene *scene,
+ ListBase *seqbase,
+ const int initial_frame,
+ const bool remove_all_gaps)
+{
+ GapInfo gap_info = {0};
+ seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info);
+
+ if (!gap_info.gap_exists) {
+ return false;
+ }
+
+ if (remove_all_gaps) {
+ while (gap_info.gap_exists) {
+ SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame);
+ seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info);
+ }
+ }
+ else {
+ SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame);
+ }
+ return true;
+}
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index a0ae6d6f16d..d9074b2a683 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -351,3 +351,85 @@ float BKE_sequence_get_fps(Scene *scene, Sequence *seq)
}
return 0.0f;
}
+
+/**
+ * Define boundary rectangle of sequencer timeline and fill in rect data
+ *
+ * \param scene: Scene in which strips are located
+ * \param seqbase: ListBase in which strips are located
+ * \param rect: data structure describing rectangle, that will be filled in by this function
+ */
+void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect)
+{
+ rect->xmin = scene->r.sfra;
+ rect->xmax = scene->r.efra + 1;
+ rect->ymin = 0.0f;
+ rect->ymax = 8.0f;
+
+ if (seqbase == NULL) {
+ return;
+ }
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (rect->xmin > seq->startdisp - 1) {
+ rect->xmin = seq->startdisp - 1;
+ }
+ if (rect->xmax < seq->enddisp + 1) {
+ rect->xmax = seq->enddisp + 1;
+ }
+ if (rect->ymax < seq->machine + 2) {
+ rect->ymax = seq->machine + 2;
+ }
+ }
+}
+
+/**
+ * Find first gap between strips after initial_frame and describe it by filling data of r_gap_info
+ *
+ * \param scene: Scene in which strips are located
+ * \param seqbase: ListBase in which strips are located
+ * \param initial_frame: frame on timeline from where gaps are searched for
+ * \param r_gap_info: data structure describing gap, that will be filled in by this function
+ */
+void seq_time_gap_info_get(const Scene *scene,
+ ListBase *seqbase,
+ const int initial_frame,
+ GapInfo *r_gap_info)
+{
+ rctf rectf;
+ /* Get first and last frame. */
+ SEQ_timeline_boundbox(scene, seqbase, &rectf);
+ const int sfra = (int)rectf.xmin;
+ const int efra = (int)rectf.xmax;
+ int timeline_frame = initial_frame;
+ r_gap_info->gap_exists = false;
+
+ if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) {
+ /* Search backward for gap_start_frame. */
+ for (; timeline_frame >= sfra; timeline_frame--) {
+ if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ break;
+ }
+ }
+ r_gap_info->gap_start_frame = timeline_frame + 1;
+ timeline_frame = initial_frame;
+ }
+ else {
+ /* Search forward for gap_start_frame. */
+ for (; timeline_frame <= efra; timeline_frame++) {
+ if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) {
+ r_gap_info->gap_start_frame = timeline_frame;
+ break;
+ }
+ }
+ }
+ /* Search forward for gap_end_frame. */
+ for (; timeline_frame <= efra; timeline_frame++) {
+ if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ const int gap_end_frame = timeline_frame;
+ r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame;
+ r_gap_info->gap_exists = true;
+ break;
+ }
+ }
+}
diff --git a/source/blender/sequencer/intern/strip_time.h b/source/blender/sequencer/intern/strip_time.h
index e4fb7f1d2ec..ca9a935bc96 100644
--- a/source/blender/sequencer/intern/strip_time.h
+++ b/source/blender/sequencer/intern/strip_time.h
@@ -27,12 +27,24 @@
extern "C" {
#endif
+struct ListBase;
struct Scene;
struct Sequence;
float seq_give_frame_index(struct Sequence *seq, float timeline_frame);
void seq_update_sound_bounds_recursive(struct Scene *scene, struct Sequence *metaseq);
+/* Describes gap between strips in timeline. */
+typedef struct GapInfo {
+ int gap_start_frame; /* Start frame of the gap. */
+ int gap_length; /* Length of the gap. */
+ bool gap_exists; /* False if there are no gaps. */
+} GapInfo;
+void seq_time_gap_info_get(const struct Scene *scene,
+ struct ListBase *seqbase,
+ const int initial_frame,
+ struct GapInfo *r_gap_info);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 233f8e5b22e..4aabe87bce1 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -397,3 +397,33 @@ bool BKE_sequence_base_shuffle_time(ListBase *seqbasep,
return offset ? false : true;
}
+
+/**
+ * Move strips and markers (if not locked) that start after timeline_frame by delta frames
+ *
+ * \param scene: Scene in which strips are located
+ * \param seqbase: ListBase in which strips are located
+ * \param delta: offset in frames to be applied
+ * \param timeline_frame: frame on timeline from where strips are moved
+ */
+void SEQ_offset_after_frame(Scene *scene,
+ ListBase *seqbase,
+ const int delta,
+ const int timeline_frame)
+{
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (seq->startdisp >= timeline_frame) {
+ BKE_sequence_translate(scene, seq, delta);
+ BKE_sequence_calc(scene, seq);
+ BKE_sequence_invalidate_cache_preprocessed(scene, seq);
+ }
+ }
+
+ if (!scene->toolsettings->lock_markers) {
+ LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
+ if (marker->frame >= timeline_frame) {
+ marker->frame += delta;
+ }
+ }
+ }
+}
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index 2b1d36a7709..ab0b65dba7f 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -36,6 +36,7 @@
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BLI_utildefines.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -547,3 +548,36 @@ bool sequencer_seq_generates_image(Sequence *seq)
}
return false;
}
+
+void SEQ_set_scale_to_fit(const Sequence *seq,
+ const int image_width,
+ const int image_height,
+ const int preview_width,
+ const int preview_height,
+ const eSeqImageFitMethod fit_method)
+{
+ StripTransform *transform = seq->strip->transform;
+
+ switch (fit_method) {
+ case SEQ_SCALE_TO_FIT:
+ transform->scale_x = transform->scale_y = MIN2((float)preview_width / (float)image_width,
+ (float)preview_height / (float)image_height);
+
+ break;
+ case SEQ_SCALE_TO_FILL:
+
+ transform->scale_x = transform->scale_y = MAX2((float)preview_width / (float)image_width,
+ (float)preview_height / (float)image_height);
+ break;
+ case SEQ_STRETCH_TO_FILL:
+ transform->scale_x = (float)preview_width / (float)image_width;
+ transform->scale_y = (float)preview_height / (float)image_height;
+ break;
+ case SEQ_USE_ORIGINAL_SIZE:
+ transform->scale_x = 1.0f;
+ transform->scale_y = 1.0f;
+ break;
+ }
+
+ return;
+}
diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h
index fe6041ec5e8..f30ea753d37 100644
--- a/source/blender/sequencer/intern/utils.h
+++ b/source/blender/sequencer/intern/utils.h
@@ -28,7 +28,6 @@ extern "C" {
#endif
struct Scene;
-struct anim;
bool sequencer_seq_generates_image(struct Sequence *seq);
void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile);
diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c
index c1e3b2e21cd..9a86e1e96f5 100644
--- a/source/blender/shader_fx/intern/FX_ui_common.c
+++ b/source/blender/shader_fx/intern/FX_ui_common.c
@@ -124,6 +124,59 @@ PointerRNA *shaderfx_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_
#define ERROR_LIBDATA_MESSAGE TIP_("External library data")
+static void gpencil_shaderfx_ops_extra_draw(bContext *C, uiLayout *layout, void *fx_v)
+{
+ PointerRNA op_ptr;
+ uiLayout *row;
+ ShaderFxData *fx = (ShaderFxData *)fx_v;
+
+ PointerRNA ptr;
+ Object *ob = ED_object_active_context(C);
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, &ptr);
+ uiLayoutSetContextPointer(layout, "shaderfx", &ptr);
+ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+
+ uiLayoutSetUnitsX(layout, 4.0f);
+
+ /* Duplicate. */
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"),
+ ICON_DUPLICATE,
+ "OBJECT_OT_shaderfx_copy");
+
+ uiItemS(layout);
+
+ /* Move to first. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_shaderfx_move_to_index",
+ IFACE_("Move to First"),
+ ICON_TRIA_UP,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", 0);
+ if (!fx->prev) {
+ uiLayoutSetEnabled(row, false);
+ }
+
+ /* Move to last. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "OBJECT_OT_shaderfx_move_to_index",
+ IFACE_("Move to Last"),
+ ICON_TRIA_DOWN,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->shader_fx) - 1);
+ if (!fx->next) {
+ uiLayoutSetEnabled(row, false);
+ }
+}
+
static void shaderfx_panel_header(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -159,6 +212,9 @@ static void shaderfx_panel_header(const bContext *UNUSED(C), Panel *panel)
uiItemR(row, ptr, "show_viewport", 0, "", ICON_NONE);
uiItemR(row, ptr, "show_render", 0, "", ICON_NONE);
+ /* Extra operators. */
+ uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_shaderfx_ops_extra_draw, fx);
+
row = uiLayoutRow(row, false);
uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove");
diff --git a/source/blender/simulation/SIM_mass_spring.h b/source/blender/simulation/SIM_mass_spring.h
index 42d7c86b539..43de8b155cf 100644
--- a/source/blender/simulation/SIM_mass_spring.h
+++ b/source/blender/simulation/SIM_mass_spring.h
@@ -52,7 +52,7 @@ int SIM_cloth_solve(struct Depsgraph *depsgraph,
struct ClothModifierData *clmd,
struct ListBase *effectors);
void SIM_cloth_solver_set_positions(struct ClothModifierData *clmd);
-void SIM_cloth_solver_set_volume(ClothModifierData *clmd);
+void SIM_cloth_solver_set_volume(struct ClothModifierData *clmd);
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index da2115e12fb..1f205a71338 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -671,6 +671,7 @@ struct wmDrag *WM_event_start_drag(
struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags);
void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, int sy);
void WM_drag_free(struct wmDrag *drag);
+void WM_drag_data_free(int dragtype, void *poin);
void WM_drag_free_list(struct ListBase *lb);
struct wmDropBox *WM_dropbox_add(
@@ -681,9 +682,12 @@ struct wmDropBox *WM_dropbox_add(
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
/* ID drag and drop */
-void WM_drag_add_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent);
-struct ID *WM_drag_ID(const struct wmDrag *drag, short idcode);
-struct ID *WM_drag_ID_from_event(const struct wmEvent *event, short idcode);
+void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent);
+struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode);
+struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode);
+
+struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode);
+struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode);
/* Set OpenGL viewport and scissor */
void wmViewport(const struct rcti *winrct);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 7fa2851cbf3..a2cc246e21e 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -298,6 +298,8 @@ typedef struct wmNotifier {
#define NC_LINESTYLE (23 << 24)
#define NC_CAMERA (24 << 24)
#define NC_LIGHTPROBE (25 << 24)
+/* Changes to asset data in the current .blend. */
+#define NC_ASSET (26 << 24)
/* data type, 256 entries is enough, it can overlap */
#define NOTE_DATA 0x00FF0000
@@ -408,20 +410,21 @@ typedef struct wmNotifier {
#define ND_SPACE_IMAGE (4 << 16)
#define ND_SPACE_FILE_PARAMS (5 << 16)
#define ND_SPACE_FILE_LIST (6 << 16)
-#define ND_SPACE_NODE (7 << 16)
-#define ND_SPACE_OUTLINER (8 << 16)
-#define ND_SPACE_VIEW3D (9 << 16)
-#define ND_SPACE_PROPERTIES (10 << 16)
-#define ND_SPACE_TEXT (11 << 16)
-#define ND_SPACE_TIME (12 << 16)
-#define ND_SPACE_GRAPH (13 << 16)
-#define ND_SPACE_DOPESHEET (14 << 16)
-#define ND_SPACE_NLA (15 << 16)
-#define ND_SPACE_SEQUENCER (16 << 16)
-#define ND_SPACE_NODE_VIEW (17 << 16)
-#define ND_SPACE_CHANGED (18 << 16) /*sent to a new editor type after it's replaced an old one*/
-#define ND_SPACE_CLIP (19 << 16)
-#define ND_SPACE_FILE_PREVIEW (20 << 16)
+#define ND_SPACE_ASSET_PARAMS (7 << 16)
+#define ND_SPACE_NODE (8 << 16)
+#define ND_SPACE_OUTLINER (9 << 16)
+#define ND_SPACE_VIEW3D (10 << 16)
+#define ND_SPACE_PROPERTIES (11 << 16)
+#define ND_SPACE_TEXT (12 << 16)
+#define ND_SPACE_TIME (13 << 16)
+#define ND_SPACE_GRAPH (14 << 16)
+#define ND_SPACE_DOPESHEET (15 << 16)
+#define ND_SPACE_NLA (16 << 16)
+#define ND_SPACE_SEQUENCER (17 << 16)
+#define ND_SPACE_NODE_VIEW (18 << 16)
+#define ND_SPACE_CHANGED (19 << 16) /*sent to a new editor type after it's replaced an old one*/
+#define ND_SPACE_CLIP (20 << 16)
+#define ND_SPACE_FILE_PREVIEW (21 << 16)
/* subtype, 256 entries too */
#define NOTE_SUBTYPE 0x0000FF00
@@ -834,12 +837,13 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata
/* *************** Drag and drop *************** */
#define WM_DRAG_ID 0
-#define WM_DRAG_RNA 1
-#define WM_DRAG_PATH 2
-#define WM_DRAG_NAME 3
-#define WM_DRAG_VALUE 4
-#define WM_DRAG_COLOR 5
-#define WM_DRAG_DATASTACK 6
+#define WM_DRAG_ASSET 1
+#define WM_DRAG_RNA 2
+#define WM_DRAG_PATH 3
+#define WM_DRAG_NAME 4
+#define WM_DRAG_VALUE 5
+#define WM_DRAG_COLOR 6
+#define WM_DRAG_DATASTACK 7
typedef enum wmDragFlags {
WM_DRAG_NOP = 0,
@@ -854,6 +858,13 @@ typedef struct wmDragID {
struct ID *from_parent;
} wmDragID;
+typedef struct wmDragAsset {
+ char name[64]; /* MAX_NAME */
+ /* Always freed. */
+ const char *path;
+ int id_type;
+} wmDragAsset;
+
typedef struct wmDrag {
struct wmDrag *next, *prev;
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 373360c7b92..fe2e2d92127 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -37,6 +37,7 @@
#include "BIF_glutil.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_idtype.h"
#include "GPU_shader.h"
@@ -146,16 +147,23 @@ wmDrag *WM_event_start_drag(
drag->flags = flags;
drag->icon = icon;
drag->type = type;
- if (type == WM_DRAG_PATH) {
- BLI_strncpy(drag->path, poin, FILE_MAX);
- }
- else if (type == WM_DRAG_ID) {
- if (poin) {
- WM_drag_add_ID(drag, poin, NULL);
- }
- }
- else {
- drag->poin = poin;
+ switch (type) {
+ case WM_DRAG_PATH:
+ BLI_strncpy(drag->path, poin, FILE_MAX);
+ break;
+ case WM_DRAG_ID:
+ if (poin) {
+ WM_drag_add_local_ID(drag, poin, NULL);
+ }
+ break;
+ case WM_DRAG_ASSET:
+ /* Move ownership of poin to wmDrag. */
+ drag->poin = poin;
+ drag->flags |= WM_DRAG_FREE_DATA;
+ break;
+ default:
+ drag->poin = poin;
+ break;
}
drag->value = value;
@@ -170,12 +178,26 @@ void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy)
drag->sy = sy;
}
-void WM_drag_free(wmDrag *drag)
+void WM_drag_data_free(int dragtype, void *poin)
{
- if ((drag->flags & WM_DRAG_FREE_DATA) && drag->poin) {
- MEM_freeN(drag->poin);
+ /* Don't require all the callers to have a NULL-check, just allow passing NULL. */
+ if (!poin) {
+ return;
}
+ /* Not too nice, could become a callback. */
+ if (dragtype == WM_DRAG_ASSET) {
+ wmDragAsset *asset_drag = poin;
+ MEM_freeN((void *)asset_drag->path);
+ }
+ MEM_freeN(poin);
+}
+
+void WM_drag_free(wmDrag *drag)
+{
+ if (drag->flags & WM_DRAG_FREE_DATA) {
+ WM_drag_data_free(drag->type, drag->poin);
+ }
BLI_freelistN(&drag->ids);
MEM_freeN(drag);
}
@@ -279,7 +301,7 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event)
/* ************** IDs ***************** */
-void WM_drag_add_ID(wmDrag *drag, ID *id, ID *from_parent)
+void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent)
{
/* Don't drag the same ID twice. */
LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
@@ -302,7 +324,7 @@ void WM_drag_add_ID(wmDrag *drag, ID *id, ID *from_parent)
BLI_addtail(&drag->ids, drag_id);
}
-ID *WM_drag_ID(const wmDrag *drag, short idcode)
+ID *WM_drag_get_local_ID(const wmDrag *drag, short idcode)
{
if (drag->type != WM_DRAG_ID) {
return NULL;
@@ -317,14 +339,54 @@ ID *WM_drag_ID(const wmDrag *drag, short idcode)
return (idcode == 0 || GS(id->name) == idcode) ? id : NULL;
}
-ID *WM_drag_ID_from_event(const wmEvent *event, short idcode)
+ID *WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode)
{
if (event->custom != EVT_DATA_DRAGDROP) {
return NULL;
}
ListBase *lb = event->customdata;
- return WM_drag_ID(lb->first, idcode);
+ return WM_drag_get_local_ID(lb->first, idcode);
+}
+
+wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
+{
+ if (drag->type != WM_DRAG_ASSET) {
+ return NULL;
+ }
+
+ wmDragAsset *asset_drag = drag->poin;
+ return (idcode == 0 || asset_drag->id_type == idcode) ? asset_drag : NULL;
+}
+
+static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
+{
+ /* Append only for now, wmDragAsset could have a `link` bool. */
+ return WM_file_append_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+}
+
+/**
+ * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append
+ * that depending on what was chosen by the drag-box (currently append only in fact).
+ */
+ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode)
+{
+ if (!ELEM(drag->type, WM_DRAG_ASSET, WM_DRAG_ID)) {
+ return NULL;
+ }
+
+ if (drag->type == WM_DRAG_ID) {
+ return WM_drag_get_local_ID(drag, idcode);
+ }
+
+ wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, idcode);
+ if (!asset_drag) {
+ return NULL;
+ }
+
+ /* Link/append the asset. */
+ return wm_drag_asset_id_import(asset_drag);
}
/* ************** draw ***************** */
@@ -342,7 +404,7 @@ static const char *wm_drag_name(wmDrag *drag)
{
switch (drag->type) {
case WM_DRAG_ID: {
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
bool single = (BLI_listbase_count_at_most(&drag->ids, 2) == 1);
if (single) {
@@ -353,6 +415,10 @@ static const char *wm_drag_name(wmDrag *drag)
}
break;
}
+ case WM_DRAG_ASSET: {
+ const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0);
+ return asset_drag->name;
+ }
case WM_DRAG_PATH:
case WM_DRAG_NAME:
return drag->path;
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index ac27862d507..c1ae307eb55 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1204,7 +1204,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm,
RNA_STRUCT_END;
}
else {
- LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) {
+ LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) {
wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, NULL);
@@ -2293,6 +2293,11 @@ static int wm_handler_fileselect_do(bContext *C,
}
wm_handler_op_context(C, handler, ctx_win->eventstate);
+ ScrArea *handler_area = CTX_wm_area(C);
+ /* Make sure new context area is ready, the operator callback may operate on it. */
+ if (handler_area) {
+ ED_area_do_refresh(C, handler_area);
+ }
/* Needed for #UI_popup_menu_reports. */
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 5b21b2397e7..d179cc456f4 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -2959,30 +2959,25 @@ static uiBlock *block_create_autorun_warning(struct bContext *C,
void *UNUSED(arg1))
{
wmWindowManager *wm = CTX_wm_manager(C);
- const uiStyle *style = UI_style_get_dpi();
- const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
- const int dialog_width = text_points_max * 44 * U.dpi_fac;
uiBlock *block = UI_block_begin(C, region, "autorun_warning_popup", UI_EMBOSS);
-
UI_block_flag_enable(
block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
UI_block_emboss_set(block, UI_EMBOSS);
- uiLayout *layout = UI_block_layout(
- block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, dialog_width, 0, 0, style);
+ uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_ERROR);
- /* Text and some vertical space */
+ /* Title and explanation text. */
uiLayout *col = uiLayoutColumn(layout, true);
uiItemL_ex(col,
TIP_("For security reasons, automatic execution of Python scripts "
"in this file was disabled:"),
- ICON_ERROR,
+ ICON_NONE,
true,
false);
- uiItemL_ex(col, G.autoexec_fail, ICON_BLANK1, false, true);
- uiItemL(col, TIP_("This may lead to unexpected behavior"), ICON_BLANK1);
+ uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true);
+ uiItemL(col, TIP_("This may lead to unexpected behavior"), ICON_NONE);
uiItemS(layout);
@@ -2995,7 +2990,7 @@ static uiBlock *block_create_autorun_warning(struct bContext *C,
TIP_("Permanently allow execution of scripts"),
ICON_NONE);
- uiItemS(layout);
+ uiItemS_ex(layout, 3.0f);
/* Buttons */
uiBut *but;
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 631a4d23eb5..fa658a5cdec 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -198,6 +198,8 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* TODO asset only filter? */
+
prop = RNA_def_int(
ot->srna,
"filemode",
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index a5d23365df3..a5b5f082c41 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -3998,7 +3998,7 @@ static const EnumPropertyItem *rna_id_itemf(bool *r_free,
/* Show collection color tag icons in menus. */
if (id_type == ID_GR) {
- item_tmp.icon = UI_icon_color_from_collection((Collection *)id);
+ item_tmp.icon = UI_icon_color_from_collection((struct Collection *)id);
}
RNA_enum_item_add(&item, &totitem, &item_tmp);
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index fb9c71163c8..a620accab72 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -188,7 +188,7 @@ void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
}
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
- const int half_x = win->sizex / 2;
+ const int half_x = WM_window_pixels_x(win) / 2;
/* right half of the screen */
if (r_mouse_xy[0] > half_x) {
r_mouse_xy[0] -= half_x;
@@ -196,7 +196,7 @@ void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
r_mouse_xy[0] *= 2;
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
- const int half_y = win->sizey / 2;
+ const int half_y = WM_window_pixels_y(win) / 2;
/* upper half of the screen */
if (r_mouse_xy[1] > half_y) {
r_mouse_xy[1] -= half_y;
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index baa47098bd3..c26acfc9802 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -23,7 +23,6 @@
#pragma once
-struct ARegion;
struct ReportList;
struct wmWindow;
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index b48f4bdd4b9..7db936b3e51 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -29,11 +29,11 @@ blender_include_dirs(
../blender/blenloader
../blender/depsgraph
../blender/editors/include
+ ../blender/gpu
../blender/imbuf
+ ../blender/makesdna
../blender/makesrna
../blender/render
- ../blender/gpu
- ../blender/makesdna
../blender/windowmanager
)
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 97f5df3ec09..bf949d66286 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -205,6 +205,48 @@ add_blender_test(
)
add_blender_test(
+ physics_dynamic_paint
+ ${TEST_SRC_DIR}/physics/dynamic_paint_test.blend
+ --python ${TEST_PYTHON_DIR}/physics_dynamic_paint.py
+ --
+ --run-all-tests
+)
+
+add_blender_test(
+ deform_modifiers
+ ${TEST_SRC_DIR}/modeling/deform_modifiers.blend
+ --python ${TEST_PYTHON_DIR}/deform_modifiers.py
+ --
+ --run-all-tests
+)
+
+add_blender_test(
+ physics_ocean
+ ${TEST_SRC_DIR}/physics/ocean_test.blend
+ --python ${TEST_PYTHON_DIR}/physics_ocean.py
+ --
+ --run-all-tests
+)
+
+
+add_blender_test(
+ physics_particle_system
+ ${TEST_SRC_DIR}/physics/physics_particle_test.blend
+ --python ${TEST_PYTHON_DIR}/physics_particle_system.py
+ --
+ --run-all-tests
+)
+
+# Particle Instance disabling currently broken in master
+# add_blender_test(
+# physics_particle_instance
+# ${TEST_SRC_DIR}/physics/physics_particle_instance.blend
+# --python ${TEST_PYTHON_DIR}/physics_particle_instance.py
+# --
+# --run-all-tests
+# )
+
+add_blender_test(
constraints
--python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py
--
diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py
index 50f52b958f7..c732d437b57 100644
--- a/tests/python/bevel_operator.py
+++ b/tests/python/bevel_operator.py
@@ -27,151 +27,287 @@ import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import OperatorTest
+from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
def main():
tests = [
# 0
- ['EDGE', {10}, 'Cube_test', 'Cube_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_2', 'bevel', {'offset': 0.2, 'offset_type': 'WIDTH'}],
- ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_3', 'bevel', {'offset': 0.2, 'offset_type': 'DEPTH'}],
- ['EDGE', {10}, 'Cube_test', 'Cube_result_4', 'bevel', {'offset': 0.4, 'segments': 2}],
- ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_5', 'bevel', {'offset': 0.4, 'segments': 3}],
+ MeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1',
+
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2}, 'EDGE', {10})]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'offset_type': 'DEPTH'}, 'EDGE', {8, 10, 7}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 3}, 'EDGE', {10, 7}, )]),
# 5
- ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_6', 'bevel', {'offset': 0.4, 'segments': 4}],
- ['EDGE', {0, 10, 4, 7}, 'Cube_test', 'Cube_result_7', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.2}],
- ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_8', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.25}],
- ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_9', 'bevel', {'offset': 0.4, 'segments': 6, 'profile': 0.9}],
- ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_10', 'bevel', {'offset': 0.4, 'segments': 4, 'profile': 1.0}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.4, 'segments': 5, 'profile': 0.2}, 'EDGE', {0, 10, 4, 7}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.4, 'segments': 6, 'profile': 0.9}, 'EDGE', {8, 10, 7}, )]),
+ MeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.4, 'segments': 4, 'profile': 1.0}, 'EDGE', {10, 7}, )]),
# 10
- ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_11', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 1.0}],
- ['EDGE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cube_test', 'Cube_result_12', 'bevel',
- {'offset': 0.4, 'segments': 8}],
- ['EDGE', {5}, 'Pyr4_test', 'Pyr4_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {2, 5}, 'Pyr4_test', 'Pyr4_result_2', 'bevel', {'offset': 0.2}],
- ['EDGE', {2, 3, 5}, 'Pyr4_test', 'Pyr4_result_3', 'bevel', {'offset': 0.2}],
+ MeshTest('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',
+ [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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3, 5}, )]),
# 15
- ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_4', 'bevel', {'offset': 0.2}],
- ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_5', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {2, 3}, 'Pyr4_test', 'Pyr4_result_6', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_7', 'bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}],
- ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 2, 3, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 4, 'profile': 0.15}, 'EDGE', {1, 2, 3, 5}, )]),
+ MeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
# 20
- ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_9', 'bevel',
- {'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}],
- ['EDGE', {2, 3}, 'Pyr6_test', 'Pyr6_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {8, 2, 3}, 'Pyr6_test', 'Pyr6_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, 'Pyr6_test', 'Pyr6_result_3', 'bevel',
- {'offset': 0.2, 'segments': 4, 'profile': 0.8}],
- ['EDGE', {8, 9, 3, 11}, 'Sept_test', 'Sept_result_1', 'bevel', {'offset': 0.1}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3}, )]),
+ MeshTest('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',
+ [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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.1}, 'EDGE', {8, 9, 3, 11}, )]),
# 25
- ['EDGE', {8, 9, 11}, 'Sept_test', 'Sept_result_2', 'bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}],
- ['EDGE', {2, 8, 9, 12, 13, 14}, 'Saddle_test', 'Saddle_result_1', 'bevel', {'offset': 0.3, 'segments': 5}],
- ['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}],
- ['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},
- 'Bent_test', 'Bent_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {1, 8, 9, 10, 11}, 'Bentlines_test', 'Bentlines_result_1', 'bevel', {'offset': 0.2, 'segments': 3}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.3, 'segments': 5}, 'EDGE', {2, 8, 9, 12, 13, 14}, )]),
+ MeshTest('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',
+ [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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 8, 9, 10, 11}, )]),
# 30
- ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_1', 'bevel', {'offset': 0.4, 'segments': 2}],
- ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_2', 'bevel',
- {'offset': 0.4, 'segments': 2, 'profile': 1.0}],
- ['FACE', {1, 6, 7, 8, 9, 10, 11, 12}, 'Flaretop_test', 'Flaretop_result_3', 'bevel',
- {'offset': 0.4, 'segments': 4}],
- ['EDGE', {4, 8, 10, 18, 24}, 'BentL_test', 'BentL_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {0, 1, 2, 10}, 'Wires_test', 'Wires_test_result_1', 'bevel', {'offset': 0.3}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.4, 'segments': 2, 'profile': 1.0}, 'EDGE', {26, 12, 20}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {4, 8, 10, 18, 24}, )]),
+ MeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1',
+ [OperatorSpecEditMode('bevel', {'offset': 0.3}, 'EDGE', {0, 1, 2, 10}, )]),
# 35
- ['VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 'Wires_test', 'Wires_test_result_2', 'bevel',
- {'offset': 0.3, 'affect': 'VERTICES'}],
- ['EDGE', {3, 4, 5}, 'tri', 'tri_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4, 5}, 'tri', 'tri_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {3, 4, 5}, 'tri', 'tri_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {3, 4}, 'tri', 'tri_result_4', 'bevel', {'offset': 0.2}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('tri_test_4', 'tri', 'tri_result_4',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
# 40
- ['EDGE', {3, 4}, 'tri', 'tri_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
- ['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}],
- ['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}],
- ['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
+ MeshTest('tri_test_9', 'tri', 'tri_result_9',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {1}, )]),
# 45
- ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_4', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
+ MeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
# 50
- ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_7', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_8', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_9', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 5}, )]),
+ MeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]),
# 55
- ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_4', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
+ MeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]),
+ MeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]),
# 60
- ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}],
- ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_1', 'bevel', {'offset': 0.2}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]),
+ MeshTest('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
- ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_2', 'bevel',
- {'offset': 0.2, 'segments': 2}],
- ['EDGE', {32, 35}, 'cube3', 'cube3_result_3', 'bevel', {'offset': 0.2}],
- ['EDGE', {24, 35}, 'cube3', 'cube3_result_4', 'bevel', {'offset': 0.2}],
- ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_5', 'bevel', {'offset': 0.2, 'segments': 2}],
- ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_6', 'bevel', {'offset': 0.2, 'segments': 3}],
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 35}, )]),
+ MeshTest('cube3_test_4', 'cube3', 'cube3_result_4',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {24, 35}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {24, 32, 35}, )]),
# 70
- ['EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, 'Tray', 'Tray_result_1', 'bevel', {'offset': 0.01, 'segments': 2}],
- ['EDGE', {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, 'Bumptop', 'Bumptop_result_1', 'bevel',
- {'offset': 0.1, 'segments': 4}],
- ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}],
- ['EDGE', {19, 20, 23, 15}, 'Window_test', 'Window_result_1', 'bevel', {'offset': 0.05, 'segments': 2}],
+ MeshTest('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',
+ [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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2}, 'EDGE', {16, 14, 15}, )]),
+ MeshTest('Window_test', 'Window_test', 'Window_result_1',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.05, 'segments': 2}, 'EDGE', {19, 20, 23, 15}, )]),
# 75
- ['EDGE', {8}, 'Cube_hn_test', 'Cube_hn_result_1', 'bevel', {'offset': 0.2, 'harden_normals': True}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_1', 'bevel',
- {'offset': 0.2, 'miter_outer': 'PATCH'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_2', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_3', 'bevel',
- {'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_4', 'bevel',
- {'offset': 0.2, 'miter_outer': 'ARC'}],
+ MeshTest("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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
+ MeshTest('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',
+ [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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]),
# 80
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_5', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_6', 'bevel',
- {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_7', 'bevel',
- {'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_8', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps2_test', 'Blocksteps2_result_9', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
+ MeshTest('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',
+ [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',
+ [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',
+ [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',
+ [OperatorSpecEditMode('bevel',
+ {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE',
+ {4, 7, 39, 27, 30, 31}, )]),
# 85
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps3_test', 'Blocksteps3_result_10', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_11', 'bevel',
- {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}],
- ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_12', 'bevel',
- {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}],
- ['EDGE', {1, 7}, 'Spike_test', 'Spike_result_1', 'bevel', {'offset': 0.2, 'segments': 3}]
- ]
+ MeshTest('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',
+ [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',
+ [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',
+ [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 7})])
- operator_test = OperatorTest(tests)
+ ]
+ operator_test = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
+ operator_test.do_compare = True
operator_test.run_all_tests()
break
elif cmd == "--run-test":
- index = int(command[i + 1])
- operator_test.run_test(index)
+ name = command[i + 1]
+ operator_test.do_compare = False
+ operator_test.run_test(name)
break
diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py
index b35c69b7ca5..0db6a074699 100644
--- a/tests/python/boolean_operator.py
+++ b/tests/python/boolean_operator.py
@@ -29,33 +29,53 @@ import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import OperatorTest
+from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest
def main():
tests = [
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cubecube', 'Cubecube_result_7', 'intersect',
- {'mode': 'SELECT', 'separate_mode': 'NONE', 'solver' : 'FAST'}],
- ['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
- ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}],
+
+ MeshTest('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',
+ [OperatorSpecEditMode('intersect_boolean', {'operation': 'INTERSECT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
+ MeshTest('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',
+ {'separate_mode': 'CUT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('intersect',
+ {'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
+ MeshTest('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',
+ [OperatorSpecEditMode('intersect_boolean',
+ {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {6, 7, 8, 9, 10}, )]),
+ MeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1',
+ [OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]),
+
]
- operator_test = OperatorTest(tests)
+ operator_test = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
+ operator_test.do_compare = True
operator_test.run_all_tests()
break
elif cmd == "--run-test":
- index = int(command[i + 1])
- operator_test.run_test(index)
+ name = command[i + 1]
+ operator_test.do_compare = False
+ operator_test.run_test(name)
break
diff --git a/tests/python/deform_modifiers.py b/tests/python/deform_modifiers.py
new file mode 100644
index 00000000000..7c4ea457e9d
--- /dev/null
+++ b/tests/python/deform_modifiers.py
@@ -0,0 +1,119 @@
+# ##### 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>
+
+# To run the test type: blender -b /path/to/the/blend/file --python path/to/this/py/file -- --run-all-tests -- --verbose
+# Type the above line in cmd/terminal, for example, look below
+# blender -b c:\blender-lib\deform_modifiers.blend --python c:\deform_modifiers.py -- --run-all-tests -- --verbose
+
+
+import os
+import sys
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import MeshTest, 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",
+ [DeformModifierSpec(10, [
+ ModifierSpec('surface_deform', 'SURFACE_DEFORM', {'target': bpy.data.objects["Cube"]})],
+ OperatorSpecObjectMode('surfacedeform_bind', {'modifier': 'surface_deform'}))]),
+
+ # Mesh Deform Test, finally can bind to the Target object.
+ # Actual deformation occurs by animating imitating user input.
+
+ MeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform",
+ [DeformModifierSpec(10, [ModifierSpec('mesh_deform', 'MESH_DEFORM',
+ {'object': bpy.data.objects["MeshCube"], 'precision': 2})],
+ OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform'}))]),
+
+ # Surface Deform Test, finally can bind to the Target object.
+ # Actual deformation occurs by animating imitating user input.
+
+ MeshTest("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",
+ [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",
+ [DeformModifierSpec(10, [ModifierSpec('warp', 'WARP',
+ {'object_from': bpy.data.objects["From"],
+ 'object_to': bpy.data.objects["To"],
+ })])]),
+
+ #############################################
+ # Curves Deform Modifiers
+ #############################################
+ MeshTest("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",
+ [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",
+ [DeformModifierSpec(10,
+ [ModifierSpec('curve_Hook', 'HOOK', {'object': bpy.data.objects['EmptyCurve']})])]),
+
+ MeshTest("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",
+ [DeformModifierSpec(10, [ModifierSpec('warp_curve', 'WARP',
+ {'object_from': bpy.data.objects["From_curve"],
+ 'object_to': bpy.data.objects["To_curve"]})])]),
+
+]
+
+deform_tests = RunTest(tests)
+command = list(sys.argv)
+for i, cmd in enumerate(command):
+ if cmd == "--run-all-tests":
+ deform_tests.apply_modifiers = True
+ deform_tests.do_compare = True
+ deform_tests.run_all_tests()
+ break
+ elif cmd == "--run-test":
+ deform_tests.apply_modifiers = False
+ deform_tests.do_compare = False
+ name = command[i + 1]
+ deform_tests.run_test(name)
+ break
diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py
index ba156cef8ea..24f71c4066d 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 ModifierTest, ModifierSpec
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
seed(0)
@@ -47,7 +47,7 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
ModifierSpec('array', 'ARRAY', {}),
ModifierSpec('bevel', 'BEVEL', {'width': 0.1}),
ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object, 'solver': 'FAST'}),
- ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1}),
+ ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2),
ModifierSpec('decimate', 'DECIMATE', {}),
ModifierSpec('edge split', 'EDGE_SPLIT', {}),
@@ -62,7 +62,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
# ModifierSpec('remesh', 'REMESH', {}),
# ModifierSpec('screw', 'SCREW', {}), # screw can make the test very slow. Skipping for now.
- # ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible .
ModifierSpec('solidify', 'SOLIDIFY', {}),
ModifierSpec('subsurf', 'SUBSURF', {}),
@@ -78,7 +77,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False):
def main():
-
mask_first_list = get_generate_modifiers_list("testCubeMaskFirst", randomize=True)
mask_vertex_group = "testCubeMaskFirst" + "_mask"
mask_first_list.insert(0, ModifierSpec('mask', 'MASK', {'vertex_group': mask_vertex_group}))
@@ -88,169 +86,279 @@ def main():
# List of 'Generate' modifiers on a cube
###############################
# 0
- # ["testCube", "expectedCube", get_generate_modifiers_list("testCube")],
- ["testCubeRandom", "expectedCubeRandom", get_generate_modifiers_list("testCubeRandom", randomize=True)],
- ["testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list],
-
- ["testCollapseDecimate", "expectedCollapseDecimate",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
- ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]],
- ["testPlanarDecimate", "expectedPlanarDecimate",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
- ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]],
- ["testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
- ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]],
+ # MeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")),
+ MeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom",
+ get_generate_modifiers_list("testCubeRandom", randomize=True)),
+ MeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list),
+
+ MeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
+ ModifierSpec('decimate', 'DECIMATE',
+ {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]),
+ MeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
+ ModifierSpec('decimate', 'DECIMATE',
+ {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]),
+ MeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}),
+ ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]),
# 5
- ["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["testRadialBisectMirrorHelper"]}),
- ModifierSpec('mirror3', 'MIRROR', {'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False), 'use_bisect_flip_axis': (False, True, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]],
- ["regressT58411Mirror", "expectedT58411Mirror",
- [ModifierSpec('mirror', 'MIRROR', {}),
- ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}),
- ModifierSpec('subd', 'SUBSURF', {'levels': 1})]],
-
- ["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})]],
- ["testObjectScrew", "expectedObjectScrew",
- [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}),
- ModifierSpec("screw", 'SCREW', {"angle": math.radians(600), 'steps': 32, 'iterations': 1, 'use_object_screw_offset': True, 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]],
+ MeshTest("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[
+ "testRadialBisectMirrorHelper"]}),
+ ModifierSpec('mirror3', 'MIRROR',
+ {'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",
+ [ModifierSpec('mirror', 'MIRROR', {}),
+ ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}),
+ ModifierSpec('subd', 'SUBSURF', {'levels': 1})]),
+
+ MeshTest("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",
+ [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}),
+ ModifierSpec("screw", 'SCREW',
+ {"angle": math.radians(600), 'steps': 32, 'iterations': 1,
+ 'use_object_screw_offset': True,
+ 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]),
# 9
- ["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})]],
- ["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"})]],
- ["regressT72792Weld", "expectedT72792Weld",
- [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}),
- ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]],
+ MeshTest("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",
+ [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",
+ [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}),
+ ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]),
############################################
# One 'Generate' modifier on primitive meshes
#############################################
# 12
- ["testCubeArray", "expectedCubeArray", [ModifierSpec('array', 'ARRAY', {})]],
- ["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"]})]],
- ["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)})]],
- ["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"]})]],
-
- ["testCylinderBuild", "expectedCylinderBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1})]],
+ MeshTest("CubeArray", "testCubeArray", "expectedCubeArray",
+ [ModifierSpec('array', 'ARRAY', {})]),
+ MeshTest("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",
+ [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",
+ [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",
+ [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
# 17
- ["testConeDecimate", "expectedConeDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]],
- ["testCubeEdgeSplit", "expectedCubeEdgeSplit", [ModifierSpec('edge split', 'EDGE_SPLIT', {})]],
-
- ["testSphereMirror", "expectedSphereMirror", [ModifierSpec('mirror', 'MIRROR', {})]],
- ["testLocalMirror", "expectedLocalMirror",
- [ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]],
- ["testObjectOffsetMirror", "expectedObjectOffsetMirror",
- [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]],
-
- ["testCylinderMask", "expectedCylinderMask", [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]],
- ["testConeMultiRes", "expectedConeMultiRes", [ModifierSpec('multires', 'MULTIRES', {})]],
+ MeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate",
+ [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
+ MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
+ [ModifierSpec('edge split', 'EDGE_SPLIT', {})]),
+
+ MeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror",
+ [ModifierSpec('mirror', 'MIRROR', {})]),
+ MeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror",
+ [ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]),
+ MeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror",
+ [ModifierSpec('mirror', 'MIRROR',
+ {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]),
+
+ MeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask",
+ [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]),
+ MeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes",
+ [ModifierSpec('multires', 'MULTIRES', {})]),
# 24
- ["testCubeScrew", "expectedCubeScrew", [ModifierSpec('screw', 'SCREW', {})]],
-
- ["testCubeSolidify", "expectedCubeSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {})]],
- ["testComplexSolidify", "expectedComplexSolidify",
- [ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0, 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]],
- ["regressT63063Solidify", "expectedT63063Solidify",
- [ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]],
- ["regressT61979Solidify", "expectedT61979Solidify",
- [ModifierSpec('solid', 'SOLIDIFY', {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]],
-
- ["testMonkeySubsurf", "expectedMonkeySubsurf", [ModifierSpec('subsurf', 'SUBSURF', {})]],
- ["testCatmullClarkSubdivisionSurface", "expectedCatmullClarkSubdivisionSurface",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
- ["testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]],
- ["testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
- ["testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
- [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]],
+ MeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew",
+ [ModifierSpec('screw', 'SCREW', {})]),
+
+ MeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify",
+ [ModifierSpec('solidify', 'SOLIDIFY', {})]),
+ MeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify",
+ [ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0,
+ 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]),
+ MeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify",
+ [ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]),
+ MeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify",
+ [ModifierSpec('solid', 'SOLIDIFY',
+ {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]),
+
+ MeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf",
+ [ModifierSpec('subsurf', 'SUBSURF', {})]),
+ MeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface",
+ "expectedCatmullClarkSubdivisionSurface",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
+ MeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]),
+ MeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
+ MeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface",
+ [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]),
# 34
- ["testSphereTriangulate", "expectedSphereTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]],
- ["testMonkeyWireframe", "expectedMonkeyWireframe", [ModifierSpec('wireframe', 'WIREFRAME', {})]],
- #ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible .
- ["testMergedWeld", "expectedMergedWeld",
- [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]],
- ["testMergedAllWeld", "expectedMergedAllWeld",
- [ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]],
- ["testMergedNoneWeld", "expectedMergedNoneWeld",
- [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]],
+ MeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate",
+ [ModifierSpec('triangulate', 'TRIANGULATE', {})]),
+ MeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe",
+ [ModifierSpec('wireframe', 'WIREFRAME', {})]),
+
+ # Duplicate the object, test object and expected object have same world coordinates.
+ MeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin",
+ [ModifierSpec('skin', 'SKIN', {})]),
+
+ MeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld",
+ [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]),
+ MeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld",
+ [ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]),
+ MeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld",
+ [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]),
+
#############################################
# One 'Deform' modifier on primitive meshes
#############################################
# 39
- ["testMonkeyArmature", "expectedMonkeyArmature",
- [ModifierSpec('armature', 'ARMATURE', {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]],
- ["testTorusCast", "expectedTorusCast", [ModifierSpec('cast', 'CAST', {'factor': 2.64})]],
- ["testCubeCurve", "expectedCubeCurve",
- [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]],
- ["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
- # ["testMonkeyHook", "expectedMonkeyHook",
- # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': "HookVertexGroup"})]],
+ MeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature",
+ [ModifierSpec('armature', 'ARMATURE',
+ {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]),
+ MeshTest("TorusCast", "testTorusCast", "expectedTorusCast",
+ [ModifierSpec('cast', 'CAST', {'factor': 2.64})]),
+ MeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve",
+ [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]),
+ MeshTest("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",
+ # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group':
+ # "HookVertexGroup"})]),
# 43
- #ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh
- ["testCubeLattice", "expectedCubeLattice",
- [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]],
-
# ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh
+ MeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice",
+ [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]),
+
+ MeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
+ [ModifierSpec('shrinkwrap', 'SHRINKWRAP',
+ {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]),
- # Mesh Deform Modifier requires user input, so skip.
+ MeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
+ [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]),
- # mesh_test = MeshTest("testMonkeyDeform", "expectedMonkeyDeform",[
- # ModifierSpec('mesh_deform', 'MESH_DEFORM', {'object': bpy.data.objects["testDeformStructure"]}),
- # OperatorSpec('meshdeform_bind',{'modifier':'MeshDeform'},'FACE',{i for in range(500)})
- # ] ,True)
+ MeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth",
+ [ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]),
- ["testPlaneShrinkWrap", "expectedPlaneShrinkWrap",
- [ModifierSpec('shrinkwrap', 'SHRINKWRAP', {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]],
+ # Smooth corrective requires a complex mesh.
- ["testCylinderSimpleDeform", "expectedCylinderSimpleDeform",
- [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]],
+ MeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
+ [ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]),
- ["testPlaneSmooth", "expectedPlaneSmooth",
- [ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]],
+ # Gets updated often
+ MeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave",
+ [ModifierSpec('wave', 'WAVE', {})]),
- # Smooth corrective requires a complex mesh.
+ #############################################
+ # CURVES Generate Modifiers
+ #############################################
+ # Caution: Make sure test object has no modifier in "added" state, the test may fail.
+ MeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray",
+ [ModifierSpec('array', 'ARRAY', {})]),
+
+ MeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel",
+ [ModifierSpec('bevel', 'BEVEL', {})]),
+
+ MeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild",
+ [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]),
+
+ MeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate",
+ [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]),
+
+ MeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit",
+ [ModifierSpec('edgeSplit', 'EDGE_SPLIT', {})]),
+
+ MeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror",
+ [ModifierSpec('mirror', 'MIRROR', {'use_axis': (True, True, False)})]),
+
+ MeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew",
+ [ModifierSpec('screw', 'SCREW', {})]),
+
+ MeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify",
+ [ModifierSpec('solidify', 'SOLIDIFY', {'thickness': 1})]),
+
+ MeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf",
+ [ModifierSpec('subSurf', 'SUBSURF', {})]),
+
+ MeshTest("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",
+ [ModifierSpec('weld', 'WELD', {})]),
+
+ MeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2",
+ [ModifierSpec('weld', 'WELD', {})]),
+
+ #############################################
+ # Curves Deform Modifiers
+ #############################################
+ # Test 62
+ MeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast",
+ [ModifierSpec('Cast', 'CAST', {'cast_type': 'CYLINDER', 'factor': 10})]),
+
+ MeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap",
+ [ModifierSpec('ShrinkWrap', 'SHRINKWRAP',
+ {'target': bpy.data.objects['testShrinkWrapHelperSuzanne']})]),
+
+ MeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform",
+ [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(90)})]),
- ["testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth",
- [ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]],
+ MeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth",
+ [ModifierSpec('smooth', 'SMOOTH', {'factor': 10})]),
- # Surface Deform and Warp requires user input, so skip.
+ MeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave",
+ [ModifierSpec('curve_wave', 'WAVE', {'time_offset': -1.5})]),
- # Wave - requires complex mesh, so skip.
+ MeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve",
+ [ModifierSpec('curve_Curve', 'CURVE', {'object': bpy.data.objects['NurbsCurve']})]),
]
- modifiers_test = ModifierTest(tests)
+ modifiers_test = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
modifiers_test.apply_modifiers = True
+ modifiers_test.do_compare = True
modifiers_test.run_all_tests()
break
elif cmd == "--run-test":
modifiers_test.apply_modifiers = False
- index = int(command[i + 1])
- modifiers_test.run_test(index)
+ modifiers_test.do_compare = False
+ name = command[i + 1]
+ modifiers_test.run_test(name)
break
diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py
index c85e7acf4e8..6671918a206 100644
--- a/tests/python/modules/mesh_test.py
+++ b/tests/python/modules/mesh_test.py
@@ -45,7 +45,6 @@ import functools
import inspect
import os
-
# Output from this module and from blender itself will occur during tests.
# We need to flush python so that the output is properly interleaved, otherwise
# blender's output for one test will end up showing in the middle of another test...
@@ -54,37 +53,39 @@ print = functools.partial(print, flush=True)
class ModifierSpec:
"""
- Holds one modifier and its parameters.
+ Holds a Generate or Deform or Physics modifier type and its parameters.
"""
- def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict):
+ def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end=0):
"""
Constructs a modifier spec.
:param modifier_name: str - name of object modifier, e.g. "myFirstSubsurfModif"
:param modifier_type: str - type of object modifier, e.g. "SUBSURF"
:param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
+ :param frame_end: int - frame at which simulation needs to be baked or modifier needs to be applied.
"""
self.modifier_name = modifier_name
self.modifier_type = modifier_type
self.modifier_parameters = modifier_parameters
+ self.frame_end = frame_end
def __str__(self):
return "Modifier: " + self.modifier_name + " of type " + self.modifier_type + \
" with parameters: " + str(self.modifier_parameters)
-class PhysicsSpec:
+class ParticleSystemSpec:
"""
- Holds one Physics modifier and its parameters.
+ Holds a Particle System modifier and its parameters.
"""
def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int):
"""
- Constructs a physics spec.
- :param modifier_name: str - name of object modifier, e.g. "Cloth"
- :param modifier_type: str - type of object modifier, e.g. "CLOTH"
- :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
- :param frame_end:int - the last frame of the simulation at which it is baked
+ Constructs a particle system spec.
+ :param modifier_name: str - name of object modifier, e.g. "Particles"
+ :param modifier_type: str - type of object modifier, e.g. "PARTICLE_SYSTEM"
+ :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"seed" : 1}
+ :param frame_end: int - the last frame of the simulation at which the modifier is applied
"""
self.modifier_name = modifier_name
self.modifier_type = modifier_type
@@ -96,14 +97,14 @@ class PhysicsSpec:
" with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end)
-class OperatorSpec:
+class OperatorSpecEditMode:
"""
Holds one operator and its parameters.
"""
def __init__(self, operator_name: str, operator_parameters: dict, select_mode: str, selection: set):
"""
- Constructs an operatorSpec. Raises ValueError if selec_mode is invalid.
+ Constructs an OperatorSpecEditMode. Raises ValueError if selec_mode is invalid.
:param operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill"
:param operator_parameters: dict - {name : val} dictionary containing operator parameters.
:param select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE'
@@ -121,6 +122,45 @@ class OperatorSpec:
" in selection mode: " + self.select_mode + ", selecting " + str(self.selection)
+class OperatorSpecObjectMode:
+ """
+ Holds an object operator and its parameters. Helper class for DeformModifierSpec.
+ Needed to support operations in Object Mode and not Edit Mode which is supported by OperatorSpecEditMode.
+ """
+
+ def __init__(self, operator_name: str, operator_parameters: dict):
+ """
+ :param operator_name: str - name of the object operator from bpy.ops.object, e.g. "shade_smooth" or "shape_keys"
+ :param operator_parameters: dict - contains operator parameters.
+ """
+ self.operator_name = operator_name
+ self.operator_parameters = operator_parameters
+
+ def __str__(self):
+ return "Operator: " + self.operator_name + " with parameters: " + str(self.operator_parameters)
+
+
+class DeformModifierSpec:
+ """
+ Holds a list of deform modifier and OperatorSpecObjectMode.
+ For deform modifiers which have an object operator
+ """
+
+ def __init__(self, frame_number: int, modifier_list: list, object_operator_spec: OperatorSpecObjectMode = None):
+ """
+ Constructs a Deform Modifier spec (for user input)
+ :param frame_number: int - the frame at which animated keyframe is inserted
+ :param modifier_list: ModifierSpec - contains modifiers
+ :param object_operator_spec: OperatorSpecObjectMode - contains object operators
+ """
+ self.frame_number = frame_number
+ self.modifier_list = modifier_list
+ self.object_operator_spec = object_operator_spec
+
+ def __str__(self):
+ return "Modifier: " + str(self.modifier_list) + " with object operator " + str(self.object_operator_spec)
+
+
class MeshTest:
"""
A mesh testing class targeted at testing modifiers and operators on a single object.
@@ -129,33 +169,45 @@ class MeshTest:
"""
def __init__(
- self,
- test_object_name: str,
- expected_object_name: str,
- operations_stack=None,
- apply_modifiers=False,
- threshold=None,
+ 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_object: str - Name of object of mesh type to run the operations on.
- :param expected_object: str - Name of object of mesh type that has the expected
+ :param test_name: str - unique test name identifier.
+ :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
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.
- This affects operations of type ModifierSpec only.
+ :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 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, OperatorSpec)):
- raise ValueError("Expected operation of type {} or {}. Got {}".
- format(type(ModifierSpec), type(OperatorSpec),
+ 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.threshold = threshold
+ self.test_name = test_name
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
@@ -187,22 +239,6 @@ class MeshTest:
objects = bpy.data.objects
self.expected_object = objects[expected_object_name]
- def add_modifier(self, modifier_spec: ModifierSpec):
- """
- Add a modifier to the operations stack.
- :param modifier_spec: modifier to add to the operations stack
- """
- self.operations_stack.append(modifier_spec)
- if self.verbose:
- print("Added modififier {}".format(modifier_spec))
-
- def add_operator(self, operator_spec: OperatorSpec):
- """
- Adds an operator to the operations stack.
- :param operator_spec: OperatorSpec - operator to add to the operations stack.
- """
- self.operations_stack.append(operator_spec)
-
def _on_failed_test(self, compare_result, validation_success, evaluated_test_object):
if self.update and validation_success:
if self.verbose:
@@ -239,83 +275,164 @@ class MeshTest:
"""
return self._test_updated
- def _apply_modifier(self, test_object, modifier_spec: ModifierSpec):
+ def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name):
"""
- Add modifier to object and apply (if modifier_spec.apply_modifier is True)
+ Doing a depth first traversal of the modifier parameters and setting their values.
+ :param: modifier: Of type modifier, its altered to become a setting in recursion.
+ :param: modifier_parameters : dict or sequence, a simple/nested dictionary of modifier parameters.
+ :param: nested_settings_path : list(stack): helps in tracing path to each node.
+ """
+ if not isinstance(modifier_parameters, dict):
+ param_setting = None
+ for i, setting in enumerate(nested_settings_path):
+
+ # We want to set the attribute only when we have reached the last setting.
+ # Applying of intermediate settings is meaningless.
+ if i == len(nested_settings_path) - 1:
+ setattr(modifier, setting, modifier_parameters)
+
+ elif hasattr(modifier, setting):
+ param_setting = getattr(modifier, setting)
+ # getattr doesn't accept canvas_surfaces["Surface"], but we need to pass it to setattr.
+ if setting == "canvas_surfaces":
+ modifier = param_setting.active
+ else:
+ modifier = param_setting
+ else:
+ # Clean up first
+ bpy.ops.object.delete()
+ raise Exception("Modifier '{}' has no parameter named '{}'".
+ format(modifier_name, setting))
+
+ # It pops the current node before moving on to its sibling.
+ nested_settings_path.pop()
+ return
+
+ for key in modifier_parameters:
+ nested_settings_path.append(key)
+ 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
+ """
+ settings = []
+ modifier_name = modifier.name
+ self._set_parameters_impl(modifier, modifier_parameters, settings, modifier_name)
+
+ def _add_modifier(self, test_object, modifier_spec: ModifierSpec):
+ """
+ Add modifier to object.
:param test_object: bpy.types.Object - Blender object to apply modifier on.
:param modifier_spec: ModifierSpec - ModifierSpec object with parameters
"""
+ bakers_list = ['CLOTH', 'SOFT_BODY', 'DYNAMIC_PAINT', 'FLUID']
+ scene = bpy.context.scene
+ scene.frame_set(1)
modifier = test_object.modifiers.new(modifier_spec.modifier_name,
modifier_spec.modifier_type)
+
+ if modifier is None:
+ raise Exception("This modifier type is already added on the Test Object, please remove it and try again.")
+
if self.verbose:
print("Created modifier '{}' of type '{}'.".
format(modifier_spec.modifier_name, modifier_spec.modifier_type))
- for param_name in modifier_spec.modifier_parameters:
- try:
- setattr(modifier, param_name, modifier_spec.modifier_parameters[param_name])
- if self.verbose:
- print("\t set parameter '{}' with value '{}'".
- format(param_name, modifier_spec.modifier_parameters[param_name]))
- except AttributeError:
- # Clean up first
- bpy.ops.object.delete()
- raise AttributeError("Modifier '{}' has no parameter named '{}'".
- format(modifier_spec.modifier_type, param_name))
+ # Special case for Dynamic Paint, need to toggle Canvas on.
+ if modifier.type == "DYNAMIC_PAINT":
+ bpy.ops.dpaint.type_toggle(type='CANVAS')
- if self.apply_modifier:
- bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name)
+ self.set_parameters(modifier, modifier_spec.modifier_parameters)
+
+ if modifier.type in bakers_list:
+ self._bake_current_simulation(test_object, modifier.name, modifier_spec.frame_end)
+
+ scene.frame_set(modifier_spec.frame_end)
+
+ def _apply_modifier(self, test_object, modifier_name):
+ # Modifier automatically gets applied when converting from Curve to Mesh.
+ if test_object.type == 'CURVE':
+ bpy.ops.object.convert(target='MESH')
+ elif test_object.type == 'MESH':
+ bpy.ops.object.modifier_apply(modifier=modifier_name)
+ else:
+ raise Exception("This object type is not yet supported!")
+
+ def _bake_current_simulation(self, test_object, test_modifier_name, frame_end):
+ """
+ FLUID: Bakes the simulation
+ SOFT BODY, CLOTH, DYNAMIC PAINT: Overrides the point_cache context and then bakes.
+ """
- def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end):
for scene in bpy.data.scenes:
- for modifier in obj.modifiers:
- if modifier.type == test_mod_type:
- obj.modifiers[test_mod_name].point_cache.frame_end = frame_end
- override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache}
+ for modifier in test_object.modifiers:
+ if modifier.type == 'FLUID':
+ bpy.ops.fluid.bake_all()
+ break
+
+ elif modifier.type == 'CLOTH' or modifier.type == 'SOFT_BODY':
+ test_object.modifiers[test_modifier_name].point_cache.frame_end = frame_end
+ override_setting = modifier.point_cache
+ override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting}
+ bpy.ops.ptcache.bake(override, bake=True)
+ break
+
+ elif modifier.type == 'DYNAMIC_PAINT':
+ dynamic_paint_setting = modifier.canvas_settings.canvas_surfaces.active
+ override_setting = dynamic_paint_setting.point_cache
+ override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting}
bpy.ops.ptcache.bake(override, bake=True)
break
- def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec):
+ def _apply_particle_system(self, test_object, particle_sys_spec: ParticleSystemSpec):
"""
- Apply Physics settings to test objects.
+ Applies Particle System settings to test objects
"""
- scene = bpy.context.scene
- scene.frame_set(1)
- modifier = test_object.modifiers.new(physics_spec.modifier_name,
- physics_spec.modifier_type)
- physics_setting = modifier.settings
+ bpy.context.scene.frame_set(1)
+ bpy.ops.object.select_all(action='DESELECT')
+
+ test_object.modifiers.new(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type)
+
+ settings_name = test_object.particle_systems.active.settings.name
+ particle_setting = bpy.data.particles[settings_name]
if self.verbose:
print("Created modifier '{}' of type '{}'.".
- format(physics_spec.modifier_name, physics_spec.modifier_type))
+ format(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type))
- for param_name in physics_spec.modifier_parameters:
+ for param_name in particle_sys_spec.modifier_parameters:
try:
- setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name])
+ if param_name == "seed":
+ system_setting = test_object.particle_systems[particle_sys_spec.modifier_name]
+ setattr(system_setting, param_name, particle_sys_spec.modifier_parameters[param_name])
+ else:
+ setattr(particle_setting, param_name, particle_sys_spec.modifier_parameters[param_name])
+
if self.verbose:
print("\t set parameter '{}' with value '{}'".
- format(param_name, physics_spec.modifier_parameters[param_name]))
+ format(param_name, particle_sys_spec.modifier_parameters[param_name]))
except AttributeError:
# Clean up first
bpy.ops.object.delete()
raise AttributeError("Modifier '{}' has no parameter named '{}'".
- format(physics_spec.modifier_type, param_name))
-
- scene.frame_set(physics_spec.frame_end + 1)
+ format(particle_sys_spec.modifier_type, param_name))
- self._bake_current_simulation(
- test_object,
- physics_spec.modifier_type,
- physics_spec.modifier_name,
- physics_spec.frame_end,
- )
+ bpy.context.scene.frame_set(particle_sys_spec.frame_end)
+ test_object.select_set(True)
+ bpy.ops.object.duplicates_make_real()
+ test_object.select_set(True)
+ bpy.ops.object.join()
if self.apply_modifier:
- bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name)
+ self._apply_modifier(test_object, particle_sys_spec.modifier_name)
- def _apply_operator(self, test_object, operator: OperatorSpec):
+ 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: OperatorSpec - OperatorSpec object with parameters.
+ :param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters.
"""
mesh = test_object.data
bpy.ops.object.mode_set(mode='EDIT')
@@ -340,15 +457,64 @@ class MeshTest:
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type=operator.select_mode)
mesh_operator = getattr(bpy.ops.mesh, operator.operator_name)
- if not mesh_operator:
- raise AttributeError("No mesh operator {}".format(operator.operator_name))
- retval = mesh_operator(**operator.operator_parameters)
+
+ try:
+ retval = mesh_operator(**operator.operator_parameters)
+ except AttributeError:
+ raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name))
+ except TypeError as ex:
+ raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex))
+
+ if retval != {'FINISHED'}:
+ raise RuntimeError("Unexpected operator return value: {}".format(retval))
+ if self.verbose:
+ print("Applied {}".format(operator))
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ def _apply_operator_object_mode(self, operator: OperatorSpecObjectMode):
+ """
+ Applies the object operator.
+ """
+ bpy.ops.object.mode_set(mode='OBJECT')
+ object_operator = getattr(bpy.ops.object, operator.operator_name)
+
+ try:
+ retval = object_operator(**operator.operator_parameters)
+ except AttributeError:
+ raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name))
+ except TypeError as ex:
+ raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex))
+
if retval != {'FINISHED'}:
raise RuntimeError("Unexpected operator return value: {}".format(retval))
if self.verbose:
print("Applied operator {}".format(operator))
+ def _apply_deform_modifier(self, test_object, operation: list):
+ """
+ param: operation: list: List of modifiers or combination of modifier and object operator.
+ """
+
+ scene = bpy.context.scene
+ scene.frame_set(1)
bpy.ops.object.mode_set(mode='OBJECT')
+ modifier_operations_list = operation.modifier_list
+ modifier_names = []
+ object_operations = operation.object_operator_spec
+ for modifier_operations in modifier_operations_list:
+ if isinstance(modifier_operations, ModifierSpec):
+ self._add_modifier(test_object, modifier_operations)
+ modifier_names.append(modifier_operations.modifier_name)
+
+ if isinstance(object_operations, OperatorSpecObjectMode):
+ self._apply_operator_object_mode(object_operations)
+
+ scene.frame_set(operation.frame_number)
+
+ if self.apply_modifier:
+ for mod_name in modifier_names:
+ self._apply_modifier(test_object, mod_name)
def run_test(self):
"""
@@ -369,24 +535,40 @@ class MeshTest:
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._apply_modifier(evaluated_test_object, operation)
+ 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, OperatorSpec):
- self._apply_operator(evaluated_test_object, 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)
- elif isinstance(operation, PhysicsSpec):
- self._apply_physics_settings(evaluated_test_object, operation)
else:
- raise ValueError("Expected operation of type {} or {} or {}. Got {}".
- format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec),
- type(operation)))
+ 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
@@ -415,75 +597,80 @@ class MeshTest:
return self._on_failed_test(compare_result, validation_success, evaluated_test_object)
-class OperatorTest:
+class RunTest:
"""
- Helper class that stores and executes operator tests.
+ Helper class that stores and executes modifier tests.
Example usage:
+ >>> modifier_list = [
+ >>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}),
+ >>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1})
+ >>> ]
+ >>> operator_list = [
+ >>> OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE),
+ >>> ]
>>> tests = [
- >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}],
- >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}],
+ >>> MeshTest("Test1", "testCube", "expectedCube", modifier_list),
+ >>> MeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list),
+ >>> MeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list)
>>> ]
- >>> operator_test = OperatorTest(tests)
- >>> operator_test.run_all_tests()
+ >>> modifiers_test = RunTest(tests)
+ >>> modifiers_test.run_all_tests()
"""
- def __init__(self, operator_tests):
+ def __init__(self, tests, apply_modifiers=False, do_compare=False):
"""
- Constructs an operator test.
- :param operator_tests: list - list of operator test cases. Each element in the list must contain the following
+ Construct a modifier test.
+ :param tests: list - list of modifier or operator test cases. Each element in the list must contain the
+ following
in the correct order:
- 1) select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE'
- 2) selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10].
- 3) test_object_name: bpy.Types.Object - test object
- 4) expected_object_name: bpy.Types.Object - expected object
- 5) operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill"
- 6) operator_parameters: dict - {name : val} dictionary containing operator parameters.
- """
- self.operator_tests = operator_tests
+ 0) test_name: str - unique test name
+ 1) test_object_name: bpy.Types.Object - test object
+ 2) expected_object_name: bpy.Types.Object - expected object
+ 3) modifiers or operators: list - list of mesh_test.ModifierSpec objects or
+ mesh_test.OperatorSpecEditMode objects
+ """
+ self.tests = tests
+ self._ensure_unique_test_name_or_raise_error()
+ self.apply_modifiers = apply_modifiers
+ self.do_compare = do_compare
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
self._failed_tests_list = []
- def run_test(self, index: int):
+ def _ensure_unique_test_name_or_raise_error(self):
"""
- Run a single test from operator_tests list
- :param index: int - index of test
- :return: bool - True if test is successful. False otherwise.
+ Check if the test name is unique else raise an error.
"""
- case = self.operator_tests[index]
- if len(case) != 6:
- raise ValueError("Expected exactly 6 parameters for each test case, got {}".format(len(case)))
- select_mode = case[0]
- selection = case[1]
- test_object_name = case[2]
- expected_object_name = case[3]
- operator_name = case[4]
- operator_parameters = case[5]
-
- operator_spec = OperatorSpec(operator_name, operator_parameters, select_mode, selection)
+ all_test_names = []
+ for each_test in self.tests:
+ test_name = each_test.test_name
+ all_test_names.append(test_name)
- test = MeshTest(test_object_name, expected_object_name)
- test.add_operator(operator_spec)
-
- 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
+ seen_name = set()
+ for ele in all_test_names:
+ if ele in seen_name:
+ raise ValueError("{} is a duplicate, write a new unique name.".format(ele))
+ else:
+ seen_name.add(ele)
def run_all_tests(self):
- for index, _ in enumerate(self.operator_tests):
+ """
+ Run all tests in self.tests list. Raises an exception if one the tests fails.
+ """
+ for test_number, each_test in enumerate(self.tests):
+ test_name = each_test.test_name
if self.verbose:
print()
- print("Running test {}...".format(index))
- success = self.run_test(index)
+ print("Running test {}...".format(test_number))
+ print("Test name {}\n".format(test_name))
+ success = self.run_test(test_name)
if not success:
- self._failed_tests_list.append(index)
+ self._failed_tests_list.append(test_name)
if len(self._failed_tests_list) != 0:
- print("Following tests failed: {}".format(self._failed_tests_list))
+ print("\nFollowing tests failed: {}".format(self._failed_tests_list))
blender_path = bpy.app.binary_path
blend_path = bpy.data.filepath
@@ -493,63 +680,31 @@ class OperatorTest:
print("Run following command to open Blender and run the failing test:")
print("{} {} --python {} -- {} {}"
- .format(blender_path, blend_path, python_path, "--run-test", "<test_index>"))
+ .format(blender_path, blend_path, python_path, "--run-test", "<test_name>"))
raise Exception("Tests {} failed".format(self._failed_tests_list))
-
-class ModifierTest:
- """
- Helper class that stores and executes modifier tests.
-
- Example usage:
-
- >>> modifier_list = [
- >>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}),
- >>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1})
- >>> ]
- >>> tests = [
- >>> ["testCube", "expectedCube", modifier_list],
- >>> ["testCube_2", "expectedCube_2", modifier_list]
- >>> ]
- >>> modifiers_test = ModifierTest(tests)
- >>> modifiers_test.run_all_tests()
- """
-
- def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None):
+ def run_test(self, test_name: str):
"""
- Construct a modifier test.
- :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following
- in the correct order:
- 1) test_object_name: bpy.Types.Object - test object
- 2) expected_object_name: bpy.Types.Object - expected object
- 3) modifiers: list - list of mesh_test.ModifierSpec objects.
- """
- self.modifier_tests = modifier_tests
- self.apply_modifiers = apply_modifiers
- self.threshold = threshold
- self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
- self._failed_tests_list = []
-
- def run_test(self, index: int):
- """
- Run a single test from self.modifier_tests list
- :param index: int - index of test
+ Run a single test from self.tests list
+ :param test_name: int - name of test
:return: bool - True if test passed, False otherwise.
"""
- case = self.modifier_tests[index]
- if len(case) != 3:
- raise ValueError("Expected exactly 3 parameters for each test case, got {}".format(len(case)))
- test_object_name = case[0]
- expected_object_name = case[1]
- spec_list = case[2]
+ case = None
+ for index, each_test in enumerate(self.tests):
+ if test_name == each_test.test_name:
+ case = self.tests[index]
+ break
+
+ if case is None:
+ raise Exception('No test called {} found!'.format(test_name))
- test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold)
+ test = case
if self.apply_modifiers:
test.apply_modifier = True
- for modifier_spec in spec_list:
- test.add_modifier(modifier_spec)
+ if self.do_compare:
+ test.do_compare = True
success = test.run_test()
if test.is_test_updated():
@@ -557,31 +712,3 @@ class ModifierTest:
success = test.run_test()
return success
-
- def run_all_tests(self):
- """
- Run all tests in self.modifiers_tests list. Raises an exception if one the tests fails.
- """
- for index, _ in enumerate(self.modifier_tests):
- if self.verbose:
- print()
- print("Running test {}...\n".format(index))
- success = self.run_test(index)
-
- if not success:
- self._failed_tests_list.append(index)
-
- if len(self._failed_tests_list) != 0:
- print("Following tests failed: {}".format(self._failed_tests_list))
-
- blender_path = bpy.app.binary_path
- blend_path = bpy.data.filepath
- frame = inspect.stack()[1]
- module = inspect.getmodule(frame[0])
- python_path = module.__file__
-
- print("Run following command to open Blender and run the failing test:")
- print("{} {} --python {} -- {} {}"
- .format(blender_path, blend_path, python_path, "--run-test", "<test_index>"))
-
- raise Exception("Tests {} failed".format(self._failed_tests_list))
diff --git a/tests/python/operators.py b/tests/python/operators.py
index 901820c7b2d..93cdfebab7b 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 OperatorTest, OperatorSpec
+from modules.mesh_test import MeshTest, 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,
@@ -39,126 +39,181 @@ def main():
tests = [
#### 0
# bisect
- ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBisect", "expectedCubeBisect", "bisect",
- {"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, "use_fill": True}],
+ MeshTest("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
- ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBlendFromShape", "expectedCubeBlendFromShape", "blend_from_shape",
- {"shape": "Key 1"}],
+ MeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape",
+ [OperatorSpecEditMode("blend_from_shape", {"shape": "Key 1"}, 'FACE', {0, 1, 2, 3, 4, 5})]),
# bridge edge loops
- ["FACE", {0, 1}, "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", "bridge_edge_loops", {}],
+ MeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop",
+ [OperatorSpecEditMode("bridge_edge_loops", {}, "FACE", {0, 1})]),
# decimate
- ["FACE", {i for i in range(500)}, "testMonkeyDecimate", "expectedMonkeyDecimate", "decimate", {"ratio": 0.1}],
+ MeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate",
+ [OperatorSpecEditMode("decimate",
+ {"ratio": 0.1}, "FACE", {i for i in range(500)})]),
### 4
# delete
- ["VERT", {3}, "testCubeDeleteVertices", "expectedCubeDeleteVertices", "delete", {}],
- ["FACE", {0}, "testCubeDeleteFaces", "expectedCubeDeleteFaces", "delete", {}],
- ["EDGE", {0, 1, 2, 3}, "testCubeDeleteEdges", "expectedCubeDeleteEdges", "delete", {}],
+ MeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices",
+ [OperatorSpecEditMode("delete", {}, "VERT", {3})]),
+ MeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces",
+ [OperatorSpecEditMode("delete", {}, "FACE", {0})]),
+ MeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges",
+ [OperatorSpecEditMode("delete", {}, "EDGE", {0, 1, 2, 3})]),
# delete edge loop
- ["VERT", MONKEY_LOOP_VERT, "testMokneyDeleteEdgeLoopVertices", "expectedMonkeyDeleteEdgeLoopVertices",
- "delete_edgeloop", {}],
- ["EDGE", MONKEY_LOOP_EDGE, "testMokneyDeleteEdgeLoopEdges", "expectedMonkeyDeleteEdgeLoopEdges",
- "delete_edgeloop", {}],
+ MeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices",
+ "expectedMonkeyDeleteEdgeLoopVertices",
+ [OperatorSpecEditMode("delete_edgeloop", {}, "VERT", MONKEY_LOOP_VERT)]),
+
+ MeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges",
+ "expectedMonkeyDeleteEdgeLoopEdges",
+ [OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE)]),
### 9
# delete loose
- ["VERT", {i for i in range(12)}, "testCubeDeleteLooseVertices", "expectedCubeDeleteLooseVertices",
- "delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}],
- ["EDGE", {i for i in range(14)}, "testCubeDeleteLooseEdges", "expectedCubeDeleteLooseEdges",
- "delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}],
- ["FACE", {i for i in range(7)}, "testCubeDeleteLooseFaces", "expectedCubeDeleteLooseFaces",
- "delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}],
+ MeshTest("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",
+ "expectedCubeDeleteLooseEdges",
+ [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False},
+ "EDGE",
+ {i for i in range(14)})]),
+ MeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces",
+ "expectedCubeDeleteLooseFaces",
+ [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True},
+ "FACE",
+ {i for i in range(7)})]),
# dissolve degenerate
- ["VERT", {i for i in range(8)}, "testCubeDissolveDegenerate", "expectedCubeDissolveDegenerate",
- "dissolve_degenerate", {}],
+ MeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate",
+ "expectedCubeDissolveDegenerate",
+ [OperatorSpecEditMode("dissolve_degenerate", {}, "VERT", {i for i in range(8)})]),
### 13
# dissolve edges
- ["EDGE", {0, 5, 6, 9}, "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
- "dissolve_edges", {}],
+ MeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges",
+ [OperatorSpecEditMode("dissolve_edges", {}, "EDGE", {0, 5, 6, 9})]),
# dissolve faces
- ["VERT", {5, 34, 47, 49, 83, 91, 95}, "testCubeDissolveFaces", "expectedCubeDissolveFaces", "dissolve_faces",
- {}],
+ MeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces",
+ [OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})]),
### 15
# dissolve verts
- ["VERT", {16, 20, 22, 23, 25}, "testCubeDissolveVerts", "expectedCubeDissolveVerts", "dissolve_verts", {}],
+ MeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts",
+ [OperatorSpecEditMode("dissolve_verts", {}, "VERT", {16, 20, 22, 23, 25})]),
# duplicate
- ["VERT", {i for i in range(33)} - {23}, "testConeDuplicateVertices", "expectedConeDuplicateVertices",
- "duplicate", {}],
- ["VERT", {23}, "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", "duplicate", {}],
- ["FACE", {6, 9}, "testConeDuplicateFaces", "expectedConeDuplicateFaces", "duplicate", {}],
- ["EDGE", {i for i in range(64)}, "testConeDuplicateEdges", "expectedConeDuplicateEdges", "duplicate", {}],
+ MeshTest("ConeDuplicateVertices", "testConeDuplicateVertices",
+ "expectedConeDuplicateVertices",
+ [OperatorSpecEditMode("duplicate", {}, "VERT", {i for i in range(33)} - {23})]),
+
+ MeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex",
+ [OperatorSpecEditMode("duplicate", {}, "VERT", {23})]),
+ MeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces",
+ [OperatorSpecEditMode("duplicate", {}, "FACE", {6, 9})]),
+ MeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges",
+ [OperatorSpecEditMode("duplicate", {}, "EDGE", {i for i in range(64)})]),
### 20
# edge collapse
- ["EDGE", {1, 9, 4}, "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", "edge_collapse", {}],
+ MeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse",
+ [OperatorSpecEditMode("edge_collapse", {}, "EDGE", {1, 9, 4})]),
# edge face add
- ["VERT", {1, 3, 4, 5, 7}, "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", "edge_face_add", {}],
- ["VERT", {4, 5}, "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", "edge_face_add", {}],
+ MeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace",
+ [OperatorSpecEditMode("edge_face_add", {}, "VERT", {1, 3, 4, 5, 7})]),
+ MeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge",
+ [OperatorSpecEditMode("edge_face_add", {}, "VERT", {4, 5})]),
# edge rotate
- ["EDGE", {1}, "testCubeEdgeRotate", "expectedCubeEdgeRotate", "edge_rotate", {}],
+ MeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate",
+ [OperatorSpecEditMode("edge_rotate", {}, "EDGE", {1})]),
# edge split
- ["EDGE", {2, 5, 8, 11, 14, 17, 20, 23}, "testCubeEdgeSplit", "expectedCubeEdgeSplit", "edge_split", {}],
+ MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
+ [OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]),
### 25
# face make planar
- ["FACE", {i for i in range(500)}, "testMonkeyFaceMakePlanar", "expectedMonkeyFaceMakePlanar",
- "face_make_planar", {}],
+ MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar",
+ "expectedMonkeyFaceMakePlanar",
+ [OperatorSpecEditMode("face_make_planar", {}, "FACE", {i for i in range(500)})]),
# face split by edges
- ["VERT", {i for i in range(6)}, "testPlaneFaceSplitByEdges", "expectedPlaneFaceSplitByEdges",
- "face_split_by_edges", {}],
+ MeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges",
+ "expectedPlaneFaceSplitByEdges",
+ [OperatorSpecEditMode("face_split_by_edges", {}, "VERT", {i for i in range(6)})]),
# fill
- ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFill", "expectedIcosphereFill",
- "fill", {}],
- ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFillUseBeautyFalse",
- "expectedIcosphereFillUseBeautyFalse", "fill", {"use_beauty": False}],
+ MeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill",
+ [OperatorSpecEditMode("fill", {}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
+ MeshTest("IcosphereFillUseBeautyFalse",
+ "testIcosphereFillUseBeautyFalse", "expectedIcosphereFillUseBeautyFalse",
+ [OperatorSpecEditMode("fill", {"use_beauty": False}, "EDGE",
+ {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]),
# fill grid
- ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGrid", "expectedPlaneFillGrid",
- "fill_grid", {}],
- ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGridSimpleBlending",
- "expectedPlaneFillGridSimpleBlending", "fill_grid", {"use_interp_simple": True}],
+ MeshTest("PlaneFillGrid", "testPlaneFillGrid",
+ "expectedPlaneFillGrid",
+ [OperatorSpecEditMode("fill_grid", {}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
+
+ MeshTest("PlaneFillGridSimpleBlending",
+ "testPlaneFillGridSimpleBlending",
+ "expectedPlaneFillGridSimpleBlending",
+ [OperatorSpecEditMode("fill_grid", {"use_interp_simple": True}, "EDGE",
+ {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]),
### 31
# fill holes
- ["VERT", {i for i in range(481)}, "testSphereFillHoles", "expectedSphereFillHoles", "fill_holes", {"sides": 9}],
+ MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
+ [OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]),
# inset faces
- ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInset",
- "expectedCubeInset", "inset", {"thickness": 0.2}],
- ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95},
- "testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse",
- "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}, "testCubeInsetDepth",
- "expectedCubeInsetDepth", "inset", {"thickness": 0.2, "depth": 0.2}],
- ["FACE", {35, 36, 37, 45, 46, 47, 55, 56, 57}, "testGridInsetRelativeOffset", "expectedGridInsetRelativeOffset",
- "inset", {"thickness": 0.4, "use_relative_offset": True}],
+ MeshTest("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",
+ "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",
+ "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",
+ "expectedGridInsetRelativeOffset",
+ [OperatorSpecEditMode("inset", {"thickness": 0.4,
+ "use_relative_offset": True}, "FACE",
+ {35, 36, 37, 45, 46, 47, 55, 56, 57})]),
]
- operators_test = OperatorTest(tests)
+ operators_test = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
+ operators_test.do_compare = True
operators_test.run_all_tests()
break
elif cmd == "--run-test":
- operators_test.apply_modifiers = False
- index = int(command[i + 1])
- operators_test.run_test(index)
+ operators_test.do_compare = False
+ name = command[i + 1]
+ operators_test.run_test(name)
break
diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py
index 5b9151ea089..b88b4d63f9d 100644
--- a/tests/python/physics_cloth.py
+++ b/tests/python/physics_cloth.py
@@ -24,26 +24,42 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import ModifierTest, PhysicsSpec
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
def main():
test = [
- ["testCloth", "expectedCloth",
- [PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]],
+
+ MeshTest("ClothSimple", "testClothPlane", "expectedClothPlane",
+ [ModifierSpec('Cloth', 'CLOTH', {'settings': {'quality': 5}}, 15)], threshold=1e-3),
+
+ # Not reproducible
+ # MeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure",
+ # [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_pressure': True,
+ # 'uniform_pressure_force': 1}}, 16)]),
+
+ # Not reproducible
+ # MeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision",
+ # [ModifierSpec('Cloth', 'CLOTH', {'collision_settings': {'use_self_collision': True}}, 67)]),
+
+ MeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring",
+ [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_internal_springs': True}}, 10)], threshold=1e-3),
+
]
- cloth_test = ModifierTest(test, threshold=1e-3)
+ cloth_test = RunTest(test)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
cloth_test.apply_modifiers = True
+ cloth_test.do_compare = True
cloth_test.run_all_tests()
break
elif cmd == "--run-test":
cloth_test.apply_modifiers = False
- index = int(command[i + 1])
- cloth_test.run_test(index)
+ cloth_test.do_compare = False
+ name = command[i + 1]
+ cloth_test.run_test(name)
break
diff --git a/tests/python/physics_dynamic_paint.py b/tests/python/physics_dynamic_paint.py
new file mode 100644
index 00000000000..b5d09c8cb3a
--- /dev/null
+++ b/tests/python/physics_dynamic_paint.py
@@ -0,0 +1,58 @@
+# ##### 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
+
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+
+
+def main():
+ test = [
+
+ MeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane",
+ [ModifierSpec('dynamic_paint', 'DYNAMIC_PAINT',
+ {'ui_type': 'CANVAS',
+ 'canvas_settings': {'canvas_surfaces': {'surface_type': 'WAVE', 'frame_end': 15}}},
+ 15)]),
+
+ ]
+ dynamic_paint_test = RunTest(test)
+
+ command = list(sys.argv)
+ for i, cmd in enumerate(command):
+ if cmd == "--run-all-tests":
+ dynamic_paint_test.apply_modifiers = True
+ dynamic_paint_test.do_compare = True
+ dynamic_paint_test.run_all_tests()
+ break
+ elif cmd == "--run-test":
+ dynamic_paint_test.apply_modifiers = False
+ dynamic_paint_test.do_compare = False
+ name = command[i + 1]
+ dynamic_paint_test.run_test(name)
+ break
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/physics_ocean.py b/tests/python/physics_ocean.py
new file mode 100644
index 00000000000..40227d3d8d7
--- /dev/null
+++ b/tests/python/physics_ocean.py
@@ -0,0 +1,54 @@
+# ##### 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
+
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+
+
+def main():
+ test = [
+ # World coordinates of test and expected object should be same.
+ MeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean",
+ [ModifierSpec('Ocean', 'OCEAN', {})]),
+ ]
+ ocean_test = RunTest(test)
+
+ command = list(sys.argv)
+ for i, cmd in enumerate(command):
+ if cmd == "--run-all-tests":
+ ocean_test.apply_modifiers = True
+ ocean_test.do_compare = True
+ ocean_test.run_all_tests()
+ break
+ elif cmd == "--run-test":
+ ocean_test.apply_modifiers = False
+ ocean_test.do_compare = False
+ name = command[i + 1]
+ ocean_test.run_test(name)
+ break
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/physics_particle_instance.py b/tests/python/physics_particle_instance.py
new file mode 100644
index 00000000000..e12e357e4ce
--- /dev/null
+++ b/tests/python/physics_particle_instance.py
@@ -0,0 +1,56 @@
+# ##### 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
+
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
+
+
+def main():
+ test = [
+
+ MeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance",
+ [ModifierSpec('ParticleInstance', 'PARTICLE_INSTANCE', {'object': bpy.data.objects['Cube']})],
+ threshold=1e-3),
+
+ ]
+ particle_instance_test = RunTest(test)
+
+ command = list(sys.argv)
+ for i, cmd in enumerate(command):
+ if cmd == "--run-all-tests":
+ particle_instance_test.apply_modifiers = True
+ particle_instance_test.do_compare = True
+ particle_instance_test.run_all_tests()
+ break
+ elif cmd == "--run-test":
+ particle_instance_test.apply_modifiers = False
+ particle_instance_test.do_compare = False
+ name = command[i + 1]
+ particle_instance_test.run_test(name)
+ break
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/physics_particle_system.py b/tests/python/physics_particle_system.py
new file mode 100644
index 00000000000..0adc5ab1c54
--- /dev/null
+++ b/tests/python/physics_particle_system.py
@@ -0,0 +1,55 @@
+# ##### 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
+
+import bpy
+
+sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+from modules.mesh_test import RunTest, ParticleSystemSpec, MeshTest
+
+
+def main():
+ test = [
+ MeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem",
+ [ParticleSystemSpec('Particles', 'PARTICLE_SYSTEM', {'render_type': "OBJECT",
+ 'instance_object': bpy.data.objects['Cube']}, 20)], threshold=1e-3),
+
+ ]
+ particle_test = RunTest(test)
+
+ command = list(sys.argv)
+ for i, cmd in enumerate(command):
+ if cmd == "--run-all-tests":
+ particle_test.apply_modifiers = True
+ particle_test.do_compare = True
+ particle_test.run_all_tests()
+ break
+ elif cmd == "--run-test":
+ particle_test.apply_modifiers = False
+ particle_test.do_compare = False
+ name = command[i + 1]
+ particle_test.run_test(name)
+ break
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py
index 8d431be742c..985b3a29bb4 100644
--- a/tests/python/physics_softbody.py
+++ b/tests/python/physics_softbody.py
@@ -24,26 +24,31 @@ import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
-from modules.mesh_test import ModifierTest, PhysicsSpec
+from modules.mesh_test import RunTest, ModifierSpec, MeshTest
def main():
test = [
- ["testSoftBody", "expectedSoftBody",
- [PhysicsSpec('Softbody', 'SOFT_BODY', {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}, 45)]],
+
+ MeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody",
+ [ModifierSpec('Softbody', 'SOFT_BODY',
+ {'settings': {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}},
+ 45)]),
]
- softBody_test = ModifierTest(test)
+ soft_body_test = RunTest(test)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
- softBody_test.apply_modifiers = True
- softBody_test.run_all_tests()
+ soft_body_test.apply_modifiers = True
+ soft_body_test.do_compare = True
+ soft_body_test.run_all_tests()
break
elif cmd == "--run-test":
- softBody_test.apply_modifiers = False
- index = int(command[i + 1])
- softBody_test.run_test(index)
+ soft_body_test.apply_modifiers = False
+ soft_body_test.do_compare = False
+ name = command[i + 1]
+ soft_body_test.run_test(name)
break